From fbb7feea4e076e9f1263597ec49a646bc4ecf159 Mon Sep 17 00:00:00 2001 From: Bastian Schmidt Date: Sun, 8 Nov 2015 16:30:37 +0100 Subject: [PATCH 001/186] First steps for major WindowChrome refactoring --- Fluent/Controls/RibbonWindow.cs | 59 ++-- Fluent/Fluent dotNET 4.0.csproj | 10 +- Fluent/Fluent dotNET 4.5.csproj | 6 +- Fluent/Internal/WindowSizing.cs | 287 ------------------ .../Behaviours/BorderlessWindowBehaviour.cs | 148 --------- Fluent/Metro/Controls/MetroWindow.cs | 35 --- Fluent/Themes/Office2010/RibbonWindow.xaml | 8 +- Fluent/Themes/Office2013/RibbonWindow.xaml | 9 +- Fluent/Themes/Windows8/RibbonWindow.xaml | 8 +- FluentTest/App.xaml | 4 +- 10 files changed, 48 insertions(+), 526 deletions(-) delete mode 100644 Fluent/Internal/WindowSizing.cs delete mode 100644 Fluent/Metro/Behaviours/BorderlessWindowBehaviour.cs delete mode 100644 Fluent/Metro/Controls/MetroWindow.cs diff --git a/Fluent/Controls/RibbonWindow.cs b/Fluent/Controls/RibbonWindow.cs index b26b17779..d7c8bf7bc 100644 --- a/Fluent/Controls/RibbonWindow.cs +++ b/Fluent/Controls/RibbonWindow.cs @@ -12,16 +12,18 @@ namespace Fluent using System; using System.Diagnostics.CodeAnalysis; using System.Windows; + using System.Windows.Data; using System.Windows.Input; + using System.Windows.Interactivity; using System.Windows.Interop; + using Controlz.Behaviours; + using Fluent.Extensions; using Fluent.Internal; using Fluent.Metro.Native; -#if NET40 - using Microsoft.Windows.Shell; -#else - using System.Windows.Shell; -#endif + + //using WindowChrome = System.Windows.Shell.WindowChrome; + using WindowChrome = Controlz.Microsoft.Windows.Shell.WindowChrome; /// /// Represents basic window for ribbon @@ -81,7 +83,7 @@ public Thickness ResizeBorderThickness /// Using a DependencyProperty as the backing store for ResizeBorderTickness. This enables animation, styling, binding, etc... /// public static readonly DependencyProperty ResizeBorderThicknessProperty = - DependencyProperty.Register("ResizeBorderThickness", typeof(Thickness), typeof(RibbonWindow), new UIPropertyMetadata(new Thickness(8), OnWindowChromeRelevantPropertyChanged)); + DependencyProperty.Register("ResizeBorderThickness", typeof(Thickness), typeof(RibbonWindow), new UIPropertyMetadata(WindowChromeBehavior.ResizeBorderThicknessProperty.DefaultMetadata.DefaultValue, OnWindowChromeRelevantPropertyChanged)); /// /// Gets or sets glass border thickness @@ -192,8 +194,6 @@ public bool IsAutomaticCollapseEnabled public static readonly DependencyProperty IsAutomaticCollapseEnabledProperty = DependencyProperty.Register("IsAutomaticCollapseEnabled", typeof(bool), typeof(RibbonWindow), new PropertyMetadata(true)); - private readonly WindowSizing windowSizing; - #endregion #region Constructors @@ -232,8 +232,6 @@ private static object OnCoerceStyle(DependencyObject d, object basevalue) public RibbonWindow() { this.SizeChanged += this.OnSizeChanged; - - this.windowSizing = new WindowSizing(this); } #endregion @@ -250,9 +248,18 @@ protected override void OnSourceInitialized(EventArgs e) this.UpdateCanUseDwm(); - this.UpdateWindowChrome(); + this.InitializeWindowChromeBehavior(); + } - this.windowSizing.WindowInitialized(); + protected virtual void InitializeWindowChromeBehavior() + { + //var behavior = new WindowChromeBehavior(); + //BindingOperations.SetBinding(behavior, WindowChromeBehavior.CaptionHeightProperty, new Binding { Path = new PropertyPath(RibbonProperties.TitleBarHeightProperty), Source = this }); + //BindingOperations.SetBinding(behavior, WindowChromeBehavior.ResizeBorderThicknessProperty, new Binding { Path = new PropertyPath(ResizeBorderThicknessProperty), Source = this }); + //BindingOperations.SetBinding(behavior, WindowChromeBehavior.CornerRadiusProperty, new Binding { Path = new PropertyPath(CornerRadiusProperty), Source = this }); + //BindingOperations.SetBinding(behavior, WindowChromeBehavior.GlassFrameThicknessProperty, new Binding { Path = new PropertyPath(GlassBorderThicknessProperty), Source = this }); + //BindingOperations.SetBinding(behavior, WindowChromeBehavior.UseAeroCaptionButtonsProperty, new Binding { Path = new PropertyPath(CanUseDwmProperty), Source = this }); + //Interaction.GetBehaviors(this).Add(behavior); } /// @@ -281,8 +288,6 @@ private static void OnWindowChromeRelevantPropertyChanged(DependencyObject d, De { return; } - - window.UpdateWindowChrome(); } private static void OnDontUseDwmChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) @@ -321,25 +326,6 @@ private void MaintainIsCollapsed() } } - private void UpdateWindowChrome() - { - var windowChrome = WindowChrome.GetWindowChrome(this); - - if (windowChrome == null) - { - windowChrome = new WindowChrome(); - WindowChrome.SetWindowChrome(this, windowChrome); - } - - windowChrome.CaptionHeight = RibbonProperties.GetTitleBarHeight(this); - windowChrome.CornerRadius = this.CornerRadius; - windowChrome.GlassFrameThickness = this.GlassBorderThickness; - windowChrome.ResizeBorderThickness = this.ResizeBorderThickness; -#if NET45 - windowChrome.UseAeroCaptionButtons = this.CanUseDwm; -#endif - } - private void UpdateCanUseDwm() { this.CanUseDwm = NativeMethods.IsDwmEnabled() @@ -389,13 +375,6 @@ public override void OnApplyTemplate() { WindowChrome.SetIsHitTestVisibleInChrome(partWindowCommands, true); } - - // This has to be done when the theme is changed. Otherwise maximized windows have the wrong size. - if (this.WindowState == WindowState.Maximized) - { - this.WindowState = WindowState.Normal; - this.WindowState = WindowState.Maximized; - } } /// diff --git a/Fluent/Fluent dotNET 4.0.csproj b/Fluent/Fluent dotNET 4.0.csproj index 9201befcb..276cf9a8b 100644 --- a/Fluent/Fluent dotNET 4.0.csproj +++ b/Fluent/Fluent dotNET 4.0.csproj @@ -52,6 +52,9 @@ false + + ..\..\..\Controlz\src\bin\Debug\NET4\Controlz.dll + @@ -63,10 +66,6 @@ False ..\Lib\Net40\System.Windows.Interactivity.dll - - False - ..\Lib\Net40\Microsoft.Windows.Shell.dll - @@ -108,7 +107,6 @@ - @@ -118,11 +116,9 @@ - - diff --git a/Fluent/Fluent dotNET 4.5.csproj b/Fluent/Fluent dotNET 4.5.csproj index 4af4f94ba..d8d6e1163 100644 --- a/Fluent/Fluent dotNET 4.5.csproj +++ b/Fluent/Fluent dotNET 4.5.csproj @@ -52,6 +52,9 @@ false + + ..\..\..\Controlz\src\bin\Debug\NET45\Controlz.dll + @@ -104,7 +107,6 @@ - @@ -114,11 +116,9 @@ - - diff --git a/Fluent/Internal/WindowSizing.cs b/Fluent/Internal/WindowSizing.cs deleted file mode 100644 index 757246fa2..000000000 --- a/Fluent/Internal/WindowSizing.cs +++ /dev/null @@ -1,287 +0,0 @@ -namespace Fluent.Internal -{ - using System; - using System.Diagnostics; - using System.Runtime.InteropServices; - using System.Windows; - using System.Windows.Interop; - using Fluent.Metro.Native; - - /// - /// Encapsulates logic for window sizing (maximizing etc.) - /// - public class WindowSizing - { - private readonly RibbonWindow window; - private IntPtr windowHwnd; - private bool fixingWindowChromeBug; - - /// - /// Creates a new instance and binds it to - /// - public WindowSizing(RibbonWindow window) - { - this.window = window; - - this.window.StateChanged += this.HandleWindowStateChanged; - } - - /// - /// Called when has been initialize - /// - public void WindowInitialized() - { - var hwndSource = PresentationSource.FromVisual(this.window) as HwndSource; - if (hwndSource != null) - { - this.windowHwnd = hwndSource.Handle; - hwndSource.AddHook(this.HwndHook); - - this.window.Dispatcher.BeginInvoke((Action)(this.FixWindowChromeBug)); - } - } - - private void HandleWindowStateChanged(object sender, EventArgs e) - { - this.window.Dispatcher.BeginInvoke((Action)(this.FixWindowChromeBug)); - } - - private void FixWindowChromeBug() - { - if (this.fixingWindowChromeBug) - { - return; - } - - this.fixingWindowChromeBug = true; - - if (this.window.WindowState == WindowState.Maximized) - { - this.FixWindowChromeBugForMaximizedWindow(); - } - else if (this.window.SizeToContent == SizeToContent.WidthAndHeight) - { - // SizeToContent is reset to manual as soon as the window is resized anyway. - // By changing it to manual early on we avoid black areas by refreshing the window. - this.window.SizeToContent = SizeToContent.Manual; - } - - this.fixingWindowChromeBug = false; - } - - private IntPtr HwndHook(IntPtr hWnd, int message, IntPtr wParam, IntPtr lParam, ref bool handled) - { - var returnval = IntPtr.Zero; - - switch (message) - { - case Constants.WM_SIZE: - // When window is maximized and was not responding, windows issues another WM_SIZE when window is responding again so we need to shift the window position back and forth - // Fixes #80, #159 and #170 - if (this.GetWindowPlacement().showCmd == 3) - { - this.FixWindowChromeBugForMaximizedWindow(); - } - break; - - case Constants.WM_GETMINMAXINFO: - this.FixMinMaxInfo(hWnd, lParam, out handled); - break; - } - - return returnval; - } - - private WINDOWPLACEMENT GetWindowPlacement() - { - WINDOWPLACEMENT windowPlacement; - UnsafeNativeMethods.GetWindowPlacement(this.windowHwnd, out windowPlacement); - return windowPlacement; - } - - #region Fixes - - private void FixMinMaxInfo(IntPtr hWnd, IntPtr lParam, out bool handled) - { - if (this.GetWindowPlacement().showCmd == 3) - { - /* http://blogs.msdn.com/b/llobo/archive/2006/08/01/maximizing-window-_2800_with-windowstyle_3d00_none_2900_-considering-taskbar.aspx */ - this.WmGetMinMaxInfo(hWnd, lParam); - - handled = true; - return; - } - - handled = false; - } - - private void FixWindowChromeBugForMaximizedWindow() - { - var mmi = this.GetMinMaxInfo(this.windowHwnd, new MINMAXINFO()); - if (NativeMethods.IsDwmEnabled()) - { - UnsafeNativeMethods.MoveWindow(this.windowHwnd, mmi.ptMaxPosition.X + 10, mmi.ptMaxPosition.Y, mmi.ptMaxSize.X, mmi.ptMaxSize.Y, true); - UnsafeNativeMethods.MoveWindow(this.windowHwnd, mmi.ptMaxPosition.X, mmi.ptMaxPosition.Y, mmi.ptMaxSize.X, mmi.ptMaxSize.Y, true); - } - else - { - UnsafeNativeMethods.MoveWindow(this.windowHwnd, mmi.ptMaxPosition.X, mmi.ptMaxPosition.Y + 1, mmi.ptMaxSize.X, mmi.ptMaxSize.Y, true); - UnsafeNativeMethods.MoveWindow(this.windowHwnd, mmi.ptMaxPosition.X, mmi.ptMaxPosition.Y, mmi.ptMaxSize.X, mmi.ptMaxSize.Y, true); - } - } - - #endregion - - #region WindowSize - - private bool IgnoreTaskBar() - { - //var ignoreTaskBar = this.AssociatedObject.IgnoreTaskbarOnMaximize - // || this.AssociatedObject.WindowStyle == WindowStyle.None; - - return false; - } - - private void WmGetMinMaxInfo(IntPtr hwnd, IntPtr lParam) - { - var mmi = (MINMAXINFO)Marshal.PtrToStructure(lParam, typeof(MINMAXINFO)); - - mmi = this.GetMinMaxInfo(hwnd, mmi); - - Marshal.StructureToPtr(mmi, lParam, true); - } - - private MINMAXINFO GetMinMaxInfo(IntPtr hwnd, MINMAXINFO mmi) - { - // Adjust the maximized size and position to fit the work area of the correct monitor - var monitor = UnsafeNativeMethods.MonitorFromWindow(hwnd, Constants.MONITOR_DEFAULTTONEAREST); - - if (monitor == IntPtr.Zero) - { - return mmi; - } - - var monitorInfo = new MONITORINFO(); - UnsafeNativeMethods.GetMonitorInfo(monitor, monitorInfo); - var rcWorkArea = monitorInfo.rcWork; - var rcMonitorArea = monitorInfo.rcMonitor; - - Debug.WriteLine("Monitor-Info"); - Debug.WriteLine(string.Format("Work: {0}", rcWorkArea)); - Debug.WriteLine(string.Format("Mon : {0}", rcMonitorArea)); - - Debug.WriteLine(string.Format("Before: {0}", mmi)); - - mmi.ptMaxPosition.X = rcWorkArea.left; - mmi.ptMaxPosition.Y = rcWorkArea.top; - - var ignoreTaskBar = this.IgnoreTaskBar(); - - var x = ignoreTaskBar ? monitorInfo.rcMonitor.left : monitorInfo.rcWork.left; - var y = ignoreTaskBar ? monitorInfo.rcMonitor.top : monitorInfo.rcWork.top; - var maxWidth = ignoreTaskBar ? Math.Abs(monitorInfo.rcMonitor.right - x) : Math.Abs(monitorInfo.rcWork.right - x); - var maxHeight = ignoreTaskBar ? Math.Abs(monitorInfo.rcMonitor.bottom - y) : Math.Abs(monitorInfo.rcWork.bottom - y); - - var maxWindowWidth = double.IsPositiveInfinity(this.window.MaxWidth) ? maxWidth : (int)this.window.MaxWidth; - var maxWindowHeight = double.IsPositiveInfinity(this.window.MaxHeight) ? maxHeight : (int)this.window.MaxHeight; - - mmi.ptMaxSize.X = Math.Min(maxWidth, maxWindowWidth); - mmi.ptMaxSize.Y = Math.Min(maxHeight, maxWindowHeight); - - if (!ignoreTaskBar) - { - mmi.ptMaxTrackSize.X = mmi.ptMaxSize.X; - mmi.ptMaxTrackSize.Y = mmi.ptMaxSize.Y; - mmi = AdjustWorkingAreaForAutoHide(monitor, mmi); - } - - Debug.WriteLine(string.Format("After: {0}", mmi)); - - return mmi; - } - - private static int GetEdge(RECT rc) - { - int uEdge; - - if (rc.top == rc.left - && rc.bottom > rc.right) - { - uEdge = (int)ABEdge.ABE_LEFT; - } - else if (rc.top == rc.left - && rc.bottom < rc.right) - { - uEdge = (int)ABEdge.ABE_TOP; - } - else if (rc.top > rc.left) - { - uEdge = (int)ABEdge.ABE_BOTTOM; - } - else - { - uEdge = (int)ABEdge.ABE_RIGHT; - } - - return uEdge; - } - - /// - /// This method handles the window size if the taskbar is set to auto-hide. - /// - private static MINMAXINFO AdjustWorkingAreaForAutoHide(IntPtr monitorContainingApplication, MINMAXINFO mmi) - { - var hwnd = UnsafeNativeMethods.FindWindow("Shell_TrayWnd", null); - var monitorWithTaskbarOnIt = UnsafeNativeMethods.MonitorFromWindow(hwnd, Constants.MONITOR_DEFAULTTONEAREST); - - if (monitorContainingApplication.Equals(monitorWithTaskbarOnIt) == false) - { - return mmi; - } - - var abd = new APPBARDATA(); - abd.cbSize = Marshal.SizeOf(abd); - abd.hWnd = hwnd; - UnsafeNativeMethods.SHAppBarMessage((int)ABMsg.ABM_GETTASKBARPOS, ref abd); - var uEdge = GetEdge(abd.rc); - var autoHide = UnsafeNativeMethods.SHAppBarMessage((int)ABMsg.ABM_GETSTATE, ref abd) == new IntPtr(1); - - if (!autoHide) - { - return mmi; - } - - switch (uEdge) - { - case (int)ABEdge.ABE_LEFT: - mmi.ptMaxPosition.X += 2; - mmi.ptMaxTrackSize.X -= 2; - mmi.ptMaxSize.X -= 2; - break; - - case (int)ABEdge.ABE_RIGHT: - mmi.ptMaxSize.X -= 2; - mmi.ptMaxTrackSize.X -= 2; - break; - - case (int)ABEdge.ABE_TOP: - mmi.ptMaxPosition.Y += 2; - mmi.ptMaxTrackSize.Y -= 2; - mmi.ptMaxSize.Y -= 2; - break; - - case (int)ABEdge.ABE_BOTTOM: - mmi.ptMaxSize.Y -= 2; - mmi.ptMaxTrackSize.Y -= 2; - break; - - default: - return mmi; - } - - return mmi; - } - - #endregion WindowSize - } -} \ No newline at end of file diff --git a/Fluent/Metro/Behaviours/BorderlessWindowBehaviour.cs b/Fluent/Metro/Behaviours/BorderlessWindowBehaviour.cs deleted file mode 100644 index f4f952b0d..000000000 --- a/Fluent/Metro/Behaviours/BorderlessWindowBehaviour.cs +++ /dev/null @@ -1,148 +0,0 @@ -namespace Fluent.Metro.Behaviours -{ - using System; - using System.Windows; - using System.Windows.Interactivity; - using System.Windows.Interop; - using Fluent.Metro.Native; - - /// - /// Behavior for borderless windows (used for Office 2013 theme) - /// - public class BorderlessWindowBehavior : Behavior - { - private HwndSource hwndSource; - - /// - /// Called when behavior is being attached - /// - protected override void OnAttached() - { - if (PresentationSource.FromVisual(this.AssociatedObject) != null) - { - this.AddHwndHook(); - } - else - { - this.AssociatedObject.SourceInitialized += this.HandleAssociatedObject_SourceInitialized; - } - - base.OnAttached(); - } - - /// - /// Called when behavior is being detached - /// - protected override void OnDetaching() - { - this.AssociatedObject.SourceInitialized -= this.HandleAssociatedObject_SourceInitialized; - - this.RemoveHwndHook(); - base.OnDetaching(); - } - - private void AddHwndHook() - { - this.hwndSource = PresentationSource.FromVisual(this.AssociatedObject) as HwndSource; - if (this.hwndSource != null) - { - this.hwndSource.AddHook(this.HwndHook); - } - } - - private void RemoveHwndHook() - { - this.AssociatedObject.SourceInitialized -= this.HandleAssociatedObject_SourceInitialized; - if (this.hwndSource != null) - { - this.hwndSource.RemoveHook(this.HwndHook); - } - - this.hwndSource = null; - } - - private void HandleAssociatedObject_SourceInitialized(object sender, EventArgs e) - { - this.AddHwndHook(); - } - - private IntPtr HwndHook(IntPtr hWnd, int message, IntPtr wParam, IntPtr lParam, ref bool handled) - { - var returnval = IntPtr.Zero; - - switch (message) - { - case Constants.WM_NCHITTEST: - // don't process the message on windows that are maximized as those don't have a resize border at all - if (this.AssociatedObject.WindowState == WindowState.Maximized) - { - break; - } - - // don't process the message on windows that can't be resized - var resizeMode = this.AssociatedObject.ResizeMode; - if (resizeMode == ResizeMode.CanMinimize - || resizeMode == ResizeMode.NoResize) - { - break; - } - - // get X & Y out of the message - var screenPoint = new Point(UnsafeNativeMethods.GET_X_LPARAM(lParam), UnsafeNativeMethods.GET_Y_LPARAM(lParam)); - - // convert to window coordinates - var windowPoint = this.AssociatedObject.PointFromScreen(screenPoint); - var windowSize = this.AssociatedObject.RenderSize; - var windowRect = new Rect(windowSize); - windowRect.Inflate(-6, -6); - - // don't process the message if the mouse is outside the 6px resize border - if (windowRect.Contains(windowPoint)) - { - break; - } - - var windowHeight = (int)windowSize.Height; - var windowWidth = (int)windowSize.Width; - - // create the rectangles where resize arrows are shown - var topLeft = new Rect(0, 0, 6, 6); - var top = new Rect(6, 0, windowWidth - 12, 6); - var topRight = new Rect(windowWidth - 6, 0, 6, 6); - - var left = new Rect(0, 6, 6, windowHeight - 12); - var right = new Rect(windowWidth - 6, 6, 6, windowHeight - 12); - - var bottomLeft = new Rect(0, windowHeight - 6, 6, 6); - var bottom = new Rect(6, windowHeight - 6, windowWidth - 12, 6); - var bottomRight = new Rect(windowWidth - 6, windowHeight - 6, 6, 6); - - // check if the mouse is within one of the rectangles - if (topLeft.Contains(windowPoint)) - returnval = (IntPtr)Constants.HTTOPLEFT; - else if (top.Contains(windowPoint)) - returnval = (IntPtr)Constants.HTTOP; - else if (topRight.Contains(windowPoint)) - returnval = (IntPtr)Constants.HTTOPRIGHT; - else if (left.Contains(windowPoint)) - returnval = (IntPtr)Constants.HTLEFT; - else if (right.Contains(windowPoint)) - returnval = (IntPtr)Constants.HTRIGHT; - else if (bottomLeft.Contains(windowPoint)) - returnval = (IntPtr)Constants.HTBOTTOMLEFT; - else if (bottom.Contains(windowPoint)) - returnval = (IntPtr)Constants.HTBOTTOM; - else if (bottomRight.Contains(windowPoint)) - returnval = (IntPtr)Constants.HTBOTTOMRIGHT; - - if (returnval != IntPtr.Zero) - { - handled = true; - } - - break; - } - return returnval; - } - } -} \ No newline at end of file diff --git a/Fluent/Metro/Controls/MetroWindow.cs b/Fluent/Metro/Controls/MetroWindow.cs deleted file mode 100644 index d181368e9..000000000 --- a/Fluent/Metro/Controls/MetroWindow.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Input; -using System.Windows.Interop; -using System.Windows.Media; -using Fluent.Metro.Native; - -namespace Fluent -{ - //public class MetroWindow : Window - //{ - // static MetroWindow() - // { - // DefaultStyleKeyProperty.OverrideMetadata(typeof(MetroWindow), new FrameworkPropertyMetadata(typeof(MetroWindow))); - // StyleProperty.OverrideMetadata(typeof(MetroWindow), new FrameworkPropertyMetadata(null, OnCoerceStyle)); - // } - - // private static object OnCoerceStyle(DependencyObject d, object basevalue) - // { - // if (basevalue != null) - // { - // return basevalue; - // } - - // var frameworkElement = d as FrameworkElement; - // if (frameworkElement != null) - // { - // basevalue = frameworkElement.TryFindResource(typeof(MetroWindow)); - // } - - // return basevalue; - // } - //} -} \ No newline at end of file diff --git a/Fluent/Themes/Office2010/RibbonWindow.xaml b/Fluent/Themes/Office2010/RibbonWindow.xaml index ef6f12b56..f9bf3175a 100644 --- a/Fluent/Themes/Office2010/RibbonWindow.xaml +++ b/Fluent/Themes/Office2010/RibbonWindow.xaml @@ -6,7 +6,8 @@ xmlns:PresentationOptions="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options" xmlns:System="clr-namespace:System;assembly=mscorlib" xmlns:Fluent="clr-namespace:Fluent" - xmlns:Behaviours="clr-namespace:Fluent.Metro.Behaviours"> + xmlns:Behaviours="clr-namespace:Fluent.Metro.Behaviours" + xmlns:ControlzBehaviors="clr-namespace:Controlz.Behaviours;assembly=Controlz"> + diff --git a/Fluent/Themes/Office2013/RibbonWindow.xaml b/Fluent/Themes/Office2013/RibbonWindow.xaml index c13037212..43884b924 100644 --- a/Fluent/Themes/Office2013/RibbonWindow.xaml +++ b/Fluent/Themes/Office2013/RibbonWindow.xaml @@ -2,6 +2,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Fluent="clr-namespace:Fluent" xmlns:Behaviours="clr-namespace:Fluent.Metro.Behaviours" + xmlns:ControlzBehaviors="clr-namespace:Controlz.Behaviours;assembly=Controlz" xmlns:internal="clr-namespace:Fluent.Internal" xmlns:Converters="clr-namespace:Fluent.Converters"> @@ -268,7 +269,7 @@ - - - - - - - - - - - - - - 1 - - - 2 - - - 3 - - - 4 - - - 5 - - - 6 - - - 7 - - - 8 - - - - - - - - - - - 1 - - - 2 - - - 3 - - - 4 - - - 5 - - - 6 - - - 7 - - - 8 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Fluent Ribbon Control Suite - - Fluent Ribbon Control Suite is a library that implements an Office-like (Microsoft® Office Fluent™ user interface) for the Windows Presentation Foundation (WPF). - - It provides well-customized controls such as RibbonTabControl, Backstage, Gallery, QuickAccessToolbar, ScreenTip and so on. - - It is bundled with the most up-to-date Office 2010 and Office 2013 styles. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + + + 1 + 2 + 3 + 4 + 5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This is long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long long text + + 123 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Silver + Black + Blue + Don't use DWM + + + + White + + + + White (Experimental) + + + + + Use backstage + + + Use application menu + + + + + + SaveWindowPosition + + + IsIconVisible + + + IsStatusBarVisible + + + + + + ShowQuickAccessToolBarAboveRibbon + + + CanCustomizeQuickAccessToolBar + + + CanCustomizeQuickAccessToolBarItems + + + CanCustomizeRibbon + + + IsMinimized + + + IsCollapsed + + + IsAutomaticCollapseEnabled + + + IsQuickAccessToolBarVisible + + + CanQuickAccessLocationChanging + + + AutomaticStateManagement + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Show All + Read-only + Writeable + + + + + + + + + + + + 1 + 2 + 3 + 4 + 5 + 6 + 1 (G 2) + 2 (G 2) + 3 (G 2) + + + + AAAA - First + AAAA + AAAA + AAAA + AAAA + BBBB + BBBB + BBBB + BBBB + BBBB + CCCC + CCCC + CCCC + CCCC + CCCC + DDDD + DDDD + DDDD + DDDD + DDDD + EEEE + EEEE + EEEE + EEEE + EEEE + FFFF + FFFF + FFFF + FFFF + FFFF + GGGG + GGGG + GGGG + GGGG + GGGG + HHHH + HHHH + HHHH + HHHH + HHHH + IIII + IIII + IIII + IIII + IIII + JJJJ + JJJJ + JJJJ + JJJJ + JJJJ + KKKK + KKKK + KKKK + KKKK + KKKK + LLLL + LLLL + LLLL + LLLL + LLLL + MMMM + MMMM + MMMM + MMMM + MMMM + NNNN + NNNN + NNNN + NNNN + NNNN + OOOO + OOOO + OOOO + OOOO + OOOO + PPPP + PPPP + PPPP + PPPP + PPPP + QQQQ + QQQQ + QQQQ + QQQQ + QQQQ + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + AAAA + YYYY + YYYY + YYYY + YYYY + YYYY + ZZZZ + ZZZZ + ZZZZ + ZZZZ + ZZZZ + Last + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Text 1 + Text 2 + Text 3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Test + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + 2 + + + 3 + + + 4 + + + 5 + + + 6 + + + 7 + + + 8 + + + + + + + + + + + 1 + + + 2 + + + 3 + + + 4 + + + 5 + + + 6 + + + 7 + + + 8 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Fluent Ribbon Control Suite + + Fluent Ribbon Control Suite is a library that implements an Office-like (Microsoft® Office Fluent™ user interface) for the Windows Presentation Foundation (WPF). + + It provides well-customized controls such as RibbonTabControl, Backstage, Gallery, QuickAccessToolbar, ScreenTip and so on. + + It is bundled with the most up-to-date Office 2010 and Office 2013 styles. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/FluentTest/TestContent.xaml.cs b/Fluent.Ribbon.Showcase/TestContent.xaml.cs similarity index 97% rename from FluentTest/TestContent.xaml.cs rename to Fluent.Ribbon.Showcase/TestContent.xaml.cs index cd7ff9b6b..1b13738cf 100644 --- a/FluentTest/TestContent.xaml.cs +++ b/Fluent.Ribbon.Showcase/TestContent.xaml.cs @@ -1,450 +1,450 @@ -namespace FluentTest -{ - using System; - using System.Diagnostics; - using System.Linq; - using System.Text; - using System.Threading; - using System.Windows; - using System.Windows.Controls; - using System.Windows.Input; - using System.Windows.Media; - using System.Windows.Media.Imaging; - using System.Windows.Threading; - using Fluent; - using FluentTest.ViewModels; - using Button = Fluent.Button; - - public partial class TestContent - { - private Theme? currentTheme; - private readonly MainViewModel viewModel; - - public TestContent() - { - this.InitializeComponent(); - - //Ribbon.Localization.Culture = new CultureInfo("ru-RU"); - - this.HookEvents(); - - this.viewModel = new MainViewModel(); - this.DataContext = this.viewModel; - } - - private void HookEvents() - { - this.Loaded += this.HandleTestContentLoaded; - - this.buttonBold.Checked += (s, e) => Debug.WriteLine("Checked"); - this.buttonBold.Unchecked += (s, e) => Debug.WriteLine("Unchecked"); - - this.PreviewMouseWheel += this.OnPreviewMouseWheel; - } - - private static void OnScreenTipHelpPressed(object sender, ScreenTipHelpEventArgs e) - { - Process.Start((string)e.HelpTopic); - } - - private void HandleTestContentLoaded(object sender, RoutedEventArgs e) - { - ScreenTip.HelpPressed += OnScreenTipHelpPressed; - } - - private void OnLauncherButtonClick(object sender, RoutedEventArgs e) - { - var groupBox = (RibbonGroupBox)sender; - - var wnd = new Window - { - Content = string.Format("Launcher-Window for: {0}", groupBox.Header), - Owner = Window.GetWindow(this) - }; - - wnd.Show(); - } - - private void OnSplitClick(object sender, RoutedEventArgs e) - { - MessageBox.Show("Split Clicked!!!"); - } - - private void OnEnlargeClick(object sender, RoutedEventArgs e) - { - this.InRibbonGallery.Enlarge(); - } - - private void OnReduceClick(object sender, RoutedEventArgs e) - { - this.InRibbonGallery.Reduce(); - } - - public Button CreateRibbonButton() - { - var fooCommand1 = new TestRoutedCommand(); - - var button = new Button - { - Command = fooCommand1.ItemCommand, - Header = "Foo", - Icon = new BitmapImage(new Uri(@"Images\Green.png", UriKind.Relative)), - LargeIcon = new BitmapImage(new Uri(@"Images\GreenLarge.png", UriKind.Relative)), - }; - - this.CommandBindings.Add(fooCommand1.ItemCommandBinding); - return button; - } - - #region Theming - - private enum Theme - { - Office2010, - Office2013, - Windows8 - } - - private void OnOffice2013Click(object sender, RoutedEventArgs e) - { - this.ChangeTheme(Theme.Office2013, "pack://application:,,,/Fluent;component/Themes/Office2013/Generic.xaml"); - } - - private void OnOffice2010SilverClick(object sender, RoutedEventArgs e) - { - this.ChangeTheme(Theme.Office2010, "pack://application:,,,/Fluent;component/Themes/Office2010/Silver.xaml"); - } - - private void OnOffice2010BlackClick(object sender, RoutedEventArgs e) - { - this.ChangeTheme(Theme.Office2010, "pack://application:,,,/Fluent;component/Themes/Office2010/Black.xaml"); - } - - private void OnOffice2010BlueClick(object sender, RoutedEventArgs e) - { - this.ChangeTheme(Theme.Office2010, "pack://application:,,,/Fluent;component/Themes/Office2010/Blue.xaml"); - } - - private void OnWindows8Click(object sender, RoutedEventArgs e) - { - this.ChangeTheme(Theme.Windows8, "pack://application:,,,/Fluent;component/Themes/Windows8/Silver.xaml"); - } - - - private void ChangeTheme(Theme theme, string color) - { - this.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, (ThreadStart)(() => - { - var owner = Window.GetWindow(this); - if (owner != null) - { - owner.Resources.BeginInit(); - - if (owner.Resources.MergedDictionaries.Count > 0) - { - owner.Resources.MergedDictionaries.RemoveAt(0); - } - - if (string.IsNullOrEmpty(color) == false) - { - owner.Resources.MergedDictionaries.Add(new ResourceDictionary { Source = new Uri(color) }); - } - - owner.Resources.EndInit(); - } - - if (this.currentTheme != theme) - { - Application.Current.Resources.BeginInit(); - switch (theme) - { - case Theme.Office2010: - Application.Current.Resources.MergedDictionaries.Add(new ResourceDictionary { Source = new Uri("pack://application:,,,/Fluent;component/Themes/Generic.xaml") }); - Application.Current.Resources.MergedDictionaries.RemoveAt(0); - break; - case Theme.Office2013: - Application.Current.Resources.MergedDictionaries.Add(new ResourceDictionary { Source = new Uri("pack://application:,,,/Fluent;component/Themes/Office2013/Generic.xaml") }); - Application.Current.Resources.MergedDictionaries.RemoveAt(0); - break; - case Theme.Windows8: - Application.Current.Resources.MergedDictionaries.Add(new ResourceDictionary { Source = new Uri("pack://application:,,,/Fluent;component/Themes/Windows8/Generic.xaml") }); - Application.Current.Resources.MergedDictionaries.RemoveAt(0); - break; - } - - this.currentTheme = theme; - Application.Current.Resources.EndInit(); - - if (owner is RibbonWindow) - { - owner.Style = null; - owner.Style = owner.FindResource("RibbonWindowStyle") as Style; - owner.Style = null; - - // Resize Window to work around alignment issues caused by theme change - ++owner.Width; - --owner.Width; - } - } - })); - } - - private void HandleDontUseDwmClick(object sender, RoutedEventArgs e) - { - var control = sender as UIElement; - - if (control == null) - { - return; - } - - var window = Window.GetWindow(control) as RibbonWindow; - - if (window == null) - { - return; - } - - window.DontUseDwm = this.DontUseDwm.IsChecked.GetValueOrDefault(); - } - - #endregion Theming - - #region Logical tree - - private void OnShowLogicalTreeClick(object sender, RoutedEventArgs e) - { - this.CheckLogicalTree(this.ribbon); - this.logicalTreeView.Items.Clear(); - this.BuildLogicalTree(this.ribbon, this.logicalTreeView); - } - - private static string GetDebugInfo(DependencyObject element) - { - if (element == null) - { - return "NULL"; - } - - var ribbonControl = element as IHeaderedControl; - - var header = ribbonControl != null - ? ribbonControl.Header - : string.Empty; - - var frameworkElement = element as FrameworkElement; - var name = frameworkElement != null - ? frameworkElement.Name - : string.Empty; - - return string.Format("[{0}] (Header: {1} || Name: {2})", element, header, name); - } - - private void CheckLogicalTree(DependencyObject root) - { - var children = LogicalTreeHelper.GetChildren(root); - foreach (var child in children.OfType()) - { - if (LogicalTreeHelper.GetParent(child) != root) - { - Debug.WriteLine(string.Format("Incorrect logical parent for {0}", GetDebugInfo(child))); - Debug.WriteLine(string.Format("\tExpected: {0}", GetDebugInfo(root))); - Debug.WriteLine(string.Format("\tFound: {0}", GetDebugInfo(LogicalTreeHelper.GetParent(child)))); - } - - this.CheckLogicalTree(child); - } - } - - private void BuildLogicalTree(DependencyObject current, ItemsControl parentControl) - { - var newItem = new TreeViewItem - { - Header = GetDebugInfo(current), - Tag = current - }; - - parentControl.Items.Add(newItem); - - var children = LogicalTreeHelper.GetChildren(current); - foreach (var child in children.OfType()) - { - this.BuildLogicalTree(child, newItem); - } - } - - private void OnTreeDoubleClick(object sender, MouseButtonEventArgs e) - { - var treeView = sender as TreeView; - - if (treeView == null) - { - return; - } - - var item = treeView.SelectedItem as TreeViewItem; - if (item == null) - { - return; - } - - var stringBuilder = new StringBuilder(); - this.BuildBackLogicalTree(item.Tag as DependencyObject, stringBuilder); - - MessageBox.Show(string.Format("From buttom to top:\n{0}", stringBuilder)); - } - - private void BuildBackLogicalTree(DependencyObject current, StringBuilder stringBuilder) - { - if (current == null - || current == this.ribbon) - { - return; - } - - stringBuilder.AppendFormat(" -> {0}\n", GetDebugInfo(current)); - - var parent = LogicalTreeHelper.GetParent(current); - - this.BuildBackLogicalTree(parent, stringBuilder); - } - - #endregion Logical tree - - private void OnFormatPainterClick(object sender, RoutedEventArgs e) - { - MessageBox.Show("FP"); - } - - private void OnHelpClick(object sender, RoutedEventArgs e) - { - if (this.tabGroup1.Visibility == Visibility.Visible) - { - this.tabGroup1.Visibility = Visibility.Collapsed; - this.tabGroup2.Visibility = Visibility.Collapsed; - } - else - { - this.tabGroup1.Visibility = Visibility.Visible; - this.tabGroup2.Visibility = Visibility.Visible; - } - } - - private void OnSpinnerValueChanged(object sender, RoutedPropertyChangedEventArgs e) - { - // MessageBox.Show(String.Format("Changed from {0} to {1}", e.OldValue, e.NewValue)); - } - - private void OnMenuItemClick(object sender, RoutedEventArgs e) - { - var wnd = new TestWindow - { - Owner = Window.GetWindow(this) - }; - wnd.Show(); - } - - private void OnPrintVisualClick(object sender, RoutedEventArgs e) - { - var printDlg = new PrintDialog(); - - if (printDlg.ShowDialog() == true) - { - printDlg.PrintVisual(this, "Main Window"); - } - } - - private void AddRibbonTab_OnClick(object sender, RoutedEventArgs e) - { - var tab = new RibbonTabItem - { - Header = "Test" - }; - - var group = new RibbonGroupBox(); - for (var i = 0; i < 20; i++) - { - group.Items.Add(this.CreateRibbonButton()); - } - - tab.Groups.Add(group); - - this.ribbon.Tabs.Add(tab); - } - - private void HandleSaveAsClick(object sender, RoutedEventArgs e) - { - var w = new Window(); - w.ShowDialog(); - } - - private void OpenRegularWindow_OnClick(object sender, RoutedEventArgs e) - { - new RegularWindow().Show(); - } - - private void OpenMahMetroWindow_OnClick(object sender, RoutedEventArgs e) - { - new MahMetroWindow().Show(); - } - - private void OpenRibbonWindowWithoutVisibileRibbon_OnClick(object sender, RoutedEventArgs e) - { - new RibbonWindowWithoutRibbon().Show(); - } - - private void ZoomSlider_OnValueChanged(object sender, RoutedPropertyChangedEventArgs e) - { - var textFormattingMode = e.NewValue > 1.0 || Math.Abs(e.NewValue - 1.0) < double.Epsilon ? TextFormattingMode.Ideal : TextFormattingMode.Display; - TextOptions.SetTextFormattingMode(this, textFormattingMode); - } - - private void OnPreviewMouseWheel(object sender, MouseWheelEventArgs e) - { - if (Keyboard.IsKeyDown(Key.LeftCtrl) == false - && Keyboard.IsKeyDown(Key.RightCtrl) == false) - { - return; - } - - this.zoomSlider.Value += e.Delta > 0 ? 0.1 : -0.1; - - e.Handled = true; - } - - private void SleepButton_OnClick(object sender, RoutedEventArgs e) - { - Thread.Sleep(TimeSpan.FromSeconds(10)); - } - - private void OpenModalRibbonWindow_OnClick(object sender, RoutedEventArgs e) - { - new TestWindow().ShowDialog(); - } - } - - public class TestRoutedCommand - { - public static RoutedCommand TestPresenterCommand = new RoutedCommand("TestPresenterCommand", typeof(TestRoutedCommand)); - - public ICommand ItemCommand - { - get { return TestPresenterCommand; } - } - - public CommandBinding ItemCommandBinding - { - get { return new CommandBinding(TestPresenterCommand, this.OnTestCommandExecuted, this.CanExecuteTestCommand); } - } - - private void CanExecuteTestCommand(object sender, CanExecuteRoutedEventArgs e) - { - e.CanExecute = true; - } - - private void OnTestCommandExecuted(object sender, ExecutedRoutedEventArgs e) - { - MessageBox.Show("TestPresenterCommand"); - } - } +namespace FluentTest +{ + using System; + using System.Diagnostics; + using System.Linq; + using System.Text; + using System.Threading; + using System.Windows; + using System.Windows.Controls; + using System.Windows.Input; + using System.Windows.Media; + using System.Windows.Media.Imaging; + using System.Windows.Threading; + using Fluent; + using FluentTest.ViewModels; + using Button = Fluent.Button; + + public partial class TestContent + { + private Theme? currentTheme; + private readonly MainViewModel viewModel; + + public TestContent() + { + this.InitializeComponent(); + + //Ribbon.Localization.Culture = new CultureInfo("ru-RU"); + + this.HookEvents(); + + this.viewModel = new MainViewModel(); + this.DataContext = this.viewModel; + } + + private void HookEvents() + { + this.Loaded += this.HandleTestContentLoaded; + + this.buttonBold.Checked += (s, e) => Debug.WriteLine("Checked"); + this.buttonBold.Unchecked += (s, e) => Debug.WriteLine("Unchecked"); + + this.PreviewMouseWheel += this.OnPreviewMouseWheel; + } + + private static void OnScreenTipHelpPressed(object sender, ScreenTipHelpEventArgs e) + { + Process.Start((string)e.HelpTopic); + } + + private void HandleTestContentLoaded(object sender, RoutedEventArgs e) + { + ScreenTip.HelpPressed += OnScreenTipHelpPressed; + } + + private void OnLauncherButtonClick(object sender, RoutedEventArgs e) + { + var groupBox = (RibbonGroupBox)sender; + + var wnd = new Window + { + Content = string.Format("Launcher-Window for: {0}", groupBox.Header), + Owner = Window.GetWindow(this) + }; + + wnd.Show(); + } + + private void OnSplitClick(object sender, RoutedEventArgs e) + { + MessageBox.Show("Split Clicked!!!"); + } + + private void OnEnlargeClick(object sender, RoutedEventArgs e) + { + this.InRibbonGallery.Enlarge(); + } + + private void OnReduceClick(object sender, RoutedEventArgs e) + { + this.InRibbonGallery.Reduce(); + } + + public Button CreateRibbonButton() + { + var fooCommand1 = new TestRoutedCommand(); + + var button = new Button + { + Command = fooCommand1.ItemCommand, + Header = "Foo", + Icon = new BitmapImage(new Uri(@"Images\Green.png", UriKind.Relative)), + LargeIcon = new BitmapImage(new Uri(@"Images\GreenLarge.png", UriKind.Relative)), + }; + + this.CommandBindings.Add(fooCommand1.ItemCommandBinding); + return button; + } + + #region Theming + + private enum Theme + { + Office2010, + Office2013, + Windows8 + } + + private void OnOffice2013Click(object sender, RoutedEventArgs e) + { + this.ChangeTheme(Theme.Office2013, "pack://application:,,,/Fluent;component/Themes/Office2013/Generic.xaml"); + } + + private void OnOffice2010SilverClick(object sender, RoutedEventArgs e) + { + this.ChangeTheme(Theme.Office2010, "pack://application:,,,/Fluent;component/Themes/Office2010/Silver.xaml"); + } + + private void OnOffice2010BlackClick(object sender, RoutedEventArgs e) + { + this.ChangeTheme(Theme.Office2010, "pack://application:,,,/Fluent;component/Themes/Office2010/Black.xaml"); + } + + private void OnOffice2010BlueClick(object sender, RoutedEventArgs e) + { + this.ChangeTheme(Theme.Office2010, "pack://application:,,,/Fluent;component/Themes/Office2010/Blue.xaml"); + } + + private void OnWindows8Click(object sender, RoutedEventArgs e) + { + this.ChangeTheme(Theme.Windows8, "pack://application:,,,/Fluent;component/Themes/Windows8/Silver.xaml"); + } + + + private void ChangeTheme(Theme theme, string color) + { + this.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, (ThreadStart)(() => + { + var owner = Window.GetWindow(this); + if (owner != null) + { + owner.Resources.BeginInit(); + + if (owner.Resources.MergedDictionaries.Count > 0) + { + owner.Resources.MergedDictionaries.RemoveAt(0); + } + + if (string.IsNullOrEmpty(color) == false) + { + owner.Resources.MergedDictionaries.Add(new ResourceDictionary { Source = new Uri(color) }); + } + + owner.Resources.EndInit(); + } + + if (this.currentTheme != theme) + { + Application.Current.Resources.BeginInit(); + switch (theme) + { + case Theme.Office2010: + Application.Current.Resources.MergedDictionaries.Add(new ResourceDictionary { Source = new Uri("pack://application:,,,/Fluent;component/Themes/Generic.xaml") }); + Application.Current.Resources.MergedDictionaries.RemoveAt(0); + break; + case Theme.Office2013: + Application.Current.Resources.MergedDictionaries.Add(new ResourceDictionary { Source = new Uri("pack://application:,,,/Fluent;component/Themes/Office2013/Generic.xaml") }); + Application.Current.Resources.MergedDictionaries.RemoveAt(0); + break; + case Theme.Windows8: + Application.Current.Resources.MergedDictionaries.Add(new ResourceDictionary { Source = new Uri("pack://application:,,,/Fluent;component/Themes/Windows8/Generic.xaml") }); + Application.Current.Resources.MergedDictionaries.RemoveAt(0); + break; + } + + this.currentTheme = theme; + Application.Current.Resources.EndInit(); + + if (owner is RibbonWindow) + { + owner.Style = null; + owner.Style = owner.FindResource("RibbonWindowStyle") as Style; + owner.Style = null; + + // Resize Window to work around alignment issues caused by theme change + ++owner.Width; + --owner.Width; + } + } + })); + } + + private void HandleDontUseDwmClick(object sender, RoutedEventArgs e) + { + var control = sender as UIElement; + + if (control == null) + { + return; + } + + var window = Window.GetWindow(control) as RibbonWindow; + + if (window == null) + { + return; + } + + window.DontUseDwm = this.DontUseDwm.IsChecked.GetValueOrDefault(); + } + + #endregion Theming + + #region Logical tree + + private void OnShowLogicalTreeClick(object sender, RoutedEventArgs e) + { + this.CheckLogicalTree(this.ribbon); + this.logicalTreeView.Items.Clear(); + this.BuildLogicalTree(this.ribbon, this.logicalTreeView); + } + + private static string GetDebugInfo(DependencyObject element) + { + if (element == null) + { + return "NULL"; + } + + var ribbonControl = element as IHeaderedControl; + + var header = ribbonControl != null + ? ribbonControl.Header + : string.Empty; + + var frameworkElement = element as FrameworkElement; + var name = frameworkElement != null + ? frameworkElement.Name + : string.Empty; + + return string.Format("[{0}] (Header: {1} || Name: {2})", element, header, name); + } + + private void CheckLogicalTree(DependencyObject root) + { + var children = LogicalTreeHelper.GetChildren(root); + foreach (var child in children.OfType()) + { + if (LogicalTreeHelper.GetParent(child) != root) + { + Debug.WriteLine(string.Format("Incorrect logical parent for {0}", GetDebugInfo(child))); + Debug.WriteLine(string.Format("\tExpected: {0}", GetDebugInfo(root))); + Debug.WriteLine(string.Format("\tFound: {0}", GetDebugInfo(LogicalTreeHelper.GetParent(child)))); + } + + this.CheckLogicalTree(child); + } + } + + private void BuildLogicalTree(DependencyObject current, ItemsControl parentControl) + { + var newItem = new TreeViewItem + { + Header = GetDebugInfo(current), + Tag = current + }; + + parentControl.Items.Add(newItem); + + var children = LogicalTreeHelper.GetChildren(current); + foreach (var child in children.OfType()) + { + this.BuildLogicalTree(child, newItem); + } + } + + private void OnTreeDoubleClick(object sender, MouseButtonEventArgs e) + { + var treeView = sender as TreeView; + + if (treeView == null) + { + return; + } + + var item = treeView.SelectedItem as TreeViewItem; + if (item == null) + { + return; + } + + var stringBuilder = new StringBuilder(); + this.BuildBackLogicalTree(item.Tag as DependencyObject, stringBuilder); + + MessageBox.Show(string.Format("From buttom to top:\n{0}", stringBuilder)); + } + + private void BuildBackLogicalTree(DependencyObject current, StringBuilder stringBuilder) + { + if (current == null + || current == this.ribbon) + { + return; + } + + stringBuilder.AppendFormat(" -> {0}\n", GetDebugInfo(current)); + + var parent = LogicalTreeHelper.GetParent(current); + + this.BuildBackLogicalTree(parent, stringBuilder); + } + + #endregion Logical tree + + private void OnFormatPainterClick(object sender, RoutedEventArgs e) + { + MessageBox.Show("FP"); + } + + private void OnHelpClick(object sender, RoutedEventArgs e) + { + if (this.tabGroup1.Visibility == Visibility.Visible) + { + this.tabGroup1.Visibility = Visibility.Collapsed; + this.tabGroup2.Visibility = Visibility.Collapsed; + } + else + { + this.tabGroup1.Visibility = Visibility.Visible; + this.tabGroup2.Visibility = Visibility.Visible; + } + } + + private void OnSpinnerValueChanged(object sender, RoutedPropertyChangedEventArgs e) + { + // MessageBox.Show(String.Format("Changed from {0} to {1}", e.OldValue, e.NewValue)); + } + + private void OnMenuItemClick(object sender, RoutedEventArgs e) + { + var wnd = new TestWindow + { + Owner = Window.GetWindow(this) + }; + wnd.Show(); + } + + private void OnPrintVisualClick(object sender, RoutedEventArgs e) + { + var printDlg = new PrintDialog(); + + if (printDlg.ShowDialog() == true) + { + printDlg.PrintVisual(this, "Main Window"); + } + } + + private void AddRibbonTab_OnClick(object sender, RoutedEventArgs e) + { + var tab = new RibbonTabItem + { + Header = "Test" + }; + + var group = new RibbonGroupBox(); + for (var i = 0; i < 20; i++) + { + group.Items.Add(this.CreateRibbonButton()); + } + + tab.Groups.Add(group); + + this.ribbon.Tabs.Add(tab); + } + + private void HandleSaveAsClick(object sender, RoutedEventArgs e) + { + var w = new Window(); + w.ShowDialog(); + } + + private void OpenRegularWindow_OnClick(object sender, RoutedEventArgs e) + { + new RegularWindow().Show(); + } + + private void OpenMahMetroWindow_OnClick(object sender, RoutedEventArgs e) + { + new MahMetroWindow().Show(); + } + + private void OpenRibbonWindowWithoutVisibileRibbon_OnClick(object sender, RoutedEventArgs e) + { + new RibbonWindowWithoutRibbon().Show(); + } + + private void ZoomSlider_OnValueChanged(object sender, RoutedPropertyChangedEventArgs e) + { + var textFormattingMode = e.NewValue > 1.0 || Math.Abs(e.NewValue - 1.0) < double.Epsilon ? TextFormattingMode.Ideal : TextFormattingMode.Display; + TextOptions.SetTextFormattingMode(this, textFormattingMode); + } + + private void OnPreviewMouseWheel(object sender, MouseWheelEventArgs e) + { + if (Keyboard.IsKeyDown(Key.LeftCtrl) == false + && Keyboard.IsKeyDown(Key.RightCtrl) == false) + { + return; + } + + this.zoomSlider.Value += e.Delta > 0 ? 0.1 : -0.1; + + e.Handled = true; + } + + private void SleepButton_OnClick(object sender, RoutedEventArgs e) + { + Thread.Sleep(TimeSpan.FromSeconds(10)); + } + + private void OpenModalRibbonWindow_OnClick(object sender, RoutedEventArgs e) + { + new TestWindow().ShowDialog(); + } + } + + public class TestRoutedCommand + { + public static RoutedCommand TestPresenterCommand = new RoutedCommand("TestPresenterCommand", typeof(TestRoutedCommand)); + + public ICommand ItemCommand + { + get { return TestPresenterCommand; } + } + + public CommandBinding ItemCommandBinding + { + get { return new CommandBinding(TestPresenterCommand, this.OnTestCommandExecuted, this.CanExecuteTestCommand); } + } + + private void CanExecuteTestCommand(object sender, CanExecuteRoutedEventArgs e) + { + e.CanExecute = true; + } + + private void OnTestCommandExecuted(object sender, ExecutedRoutedEventArgs e) + { + MessageBox.Show("TestPresenterCommand"); + } + } } \ No newline at end of file diff --git a/FluentTest/TestWindow.xaml b/Fluent.Ribbon.Showcase/TestWindow.xaml similarity index 98% rename from FluentTest/TestWindow.xaml rename to Fluent.Ribbon.Showcase/TestWindow.xaml index 06dfb7273..5bed141c0 100644 --- a/FluentTest/TestWindow.xaml +++ b/Fluent.Ribbon.Showcase/TestWindow.xaml @@ -1,18 +1,18 @@ - - + + \ No newline at end of file diff --git a/FluentTest/TestWindow.xaml.cs b/Fluent.Ribbon.Showcase/TestWindow.xaml.cs similarity index 95% rename from FluentTest/TestWindow.xaml.cs rename to Fluent.Ribbon.Showcase/TestWindow.xaml.cs index 9ae19b228..4fe7cf42e 100644 --- a/FluentTest/TestWindow.xaml.cs +++ b/Fluent.Ribbon.Showcase/TestWindow.xaml.cs @@ -1,13 +1,13 @@ -namespace FluentTest -{ - /// - /// Interaction logic for TestWindow.xaml - /// - public partial class TestWindow - { - public TestWindow() - { - this.InitializeComponent(); - } - } +namespace FluentTest +{ + /// + /// Interaction logic for TestWindow.xaml + /// + public partial class TestWindow + { + public TestWindow() + { + this.InitializeComponent(); + } + } } \ No newline at end of file diff --git a/FluentTest/ViewModels/ColorViewModel.cs b/Fluent.Ribbon.Showcase/ViewModels/ColorViewModel.cs similarity index 96% rename from FluentTest/ViewModels/ColorViewModel.cs rename to Fluent.Ribbon.Showcase/ViewModels/ColorViewModel.cs index 32bb218e0..f023769e9 100644 --- a/FluentTest/ViewModels/ColorViewModel.cs +++ b/Fluent.Ribbon.Showcase/ViewModels/ColorViewModel.cs @@ -1,55 +1,55 @@ -namespace FluentTest.ViewModels -{ - using System.Windows.Media; - - public class ColorViewModel : ViewModel - { - private Color standardColor; - private Color highlightColor; - - private readonly Color[] themeColors = { Colors.Red, Colors.Green, Colors.Blue, Colors.White, Colors.Black, Colors.Purple }; - private Color themeColor; - - public ColorViewModel() - { - this.StandardColor = Colors.Black; - this.HighlightColor = Colors.Yellow; - this.ThemeColor = Colors.Blue; - } - - public Color StandardColor - { - get { return this.standardColor; } - set - { - this.standardColor = value; - this.OnPropertyChanged("StandardColor"); - } - } - - public Color HighlightColor - { - get { return this.highlightColor; } - set - { - this.highlightColor = value; - this.OnPropertyChanged("HighlightColor"); - } - } - - public Color[] ThemeColors - { - get { return this.themeColors; } - } - - public Color ThemeColor - { - get { return this.themeColor; } - set - { - this.themeColor = value; - this.OnPropertyChanged("ThemeColor"); - } - } - } +namespace FluentTest.ViewModels +{ + using System.Windows.Media; + + public class ColorViewModel : ViewModel + { + private Color standardColor; + private Color highlightColor; + + private readonly Color[] themeColors = { Colors.Red, Colors.Green, Colors.Blue, Colors.White, Colors.Black, Colors.Purple }; + private Color themeColor; + + public ColorViewModel() + { + this.StandardColor = Colors.Black; + this.HighlightColor = Colors.Yellow; + this.ThemeColor = Colors.Blue; + } + + public Color StandardColor + { + get { return this.standardColor; } + set + { + this.standardColor = value; + this.OnPropertyChanged("StandardColor"); + } + } + + public Color HighlightColor + { + get { return this.highlightColor; } + set + { + this.highlightColor = value; + this.OnPropertyChanged("HighlightColor"); + } + } + + public Color[] ThemeColors + { + get { return this.themeColors; } + } + + public Color ThemeColor + { + get { return this.themeColor; } + set + { + this.themeColor = value; + this.OnPropertyChanged("ThemeColor"); + } + } + } } \ No newline at end of file diff --git a/FluentTest/ViewModels/FontsViewModel.cs b/Fluent.Ribbon.Showcase/ViewModels/FontsViewModel.cs similarity index 96% rename from FluentTest/ViewModels/FontsViewModel.cs rename to Fluent.Ribbon.Showcase/ViewModels/FontsViewModel.cs index acf416920..c70e00180 100644 --- a/FluentTest/ViewModels/FontsViewModel.cs +++ b/Fluent.Ribbon.Showcase/ViewModels/FontsViewModel.cs @@ -1,12 +1,12 @@ -namespace FluentTest.ViewModels -{ - public class FontsViewModel : ViewModel - { - private readonly string[] data = { "Tahoma", "Segoe UI", "Arial", "Courier New", "Symbol" }; - - public string[] FontsData - { - get { return this.data; } - } - } -} +namespace FluentTest.ViewModels +{ + public class FontsViewModel : ViewModel + { + private readonly string[] data = { "Tahoma", "Segoe UI", "Arial", "Courier New", "Symbol" }; + + public string[] FontsData + { + get { return this.data; } + } + } +} diff --git a/FluentTest/ViewModels/GalleryItemViewModel.cs b/Fluent.Ribbon.Showcase/ViewModels/GalleryItemViewModel.cs similarity index 95% rename from FluentTest/ViewModels/GalleryItemViewModel.cs rename to Fluent.Ribbon.Showcase/ViewModels/GalleryItemViewModel.cs index 4a9a41acc..ff18d965a 100644 --- a/FluentTest/ViewModels/GalleryItemViewModel.cs +++ b/Fluent.Ribbon.Showcase/ViewModels/GalleryItemViewModel.cs @@ -1,15 +1,15 @@ -namespace FluentTest.ViewModels -{ - public class GalleryItemViewModel - { - public GalleryItemViewModel(string group, string text) - { - this.Group = group; - this.Text = text; - } - - public string Text { get; set; } - - public string Group { get; set; } - } +namespace FluentTest.ViewModels +{ + public class GalleryItemViewModel + { + public GalleryItemViewModel(string group, string text) + { + this.Group = group; + this.Text = text; + } + + public string Text { get; set; } + + public string Group { get; set; } + } } \ No newline at end of file diff --git a/FluentTest/ViewModels/GallerySampleDataItemViewModel.cs b/Fluent.Ribbon.Showcase/ViewModels/GallerySampleDataItemViewModel.cs similarity index 96% rename from FluentTest/ViewModels/GallerySampleDataItemViewModel.cs rename to Fluent.Ribbon.Showcase/ViewModels/GallerySampleDataItemViewModel.cs index bc57a2a7a..48b1e7e47 100644 --- a/FluentTest/ViewModels/GallerySampleDataItemViewModel.cs +++ b/Fluent.Ribbon.Showcase/ViewModels/GallerySampleDataItemViewModel.cs @@ -1,47 +1,47 @@ -namespace FluentTest.ViewModels -{ - using System; - using System.Windows.Media; - using System.Windows.Media.Imaging; - - public class GallerySampleDataItemViewModel : ViewModel - { - /// - /// Gets or sets icon - /// - public ImageSource Icon { get; set; } - /// - /// Gets or sets large icon - /// - public ImageSource IconLarge { get; set; } - /// - /// Gets or sets text - /// - public string Text { get; set; } - /// - /// Gets or sets group name - /// - public string Group { get; set; } - - /// - /// Creates new item - /// - /// Icon - /// Large Icon - /// Text - /// Group - /// Item - public static GallerySampleDataItemViewModel Create(string icon, string iconLarge, string text, string group) - { - var dataItem = new GallerySampleDataItemViewModel - { - Icon = new BitmapImage(new Uri(icon, UriKind.Relative)), - IconLarge = new BitmapImage(new Uri(iconLarge, UriKind.Relative)), - Text = text, - Group = group - }; - - return dataItem; - } - } +namespace FluentTest.ViewModels +{ + using System; + using System.Windows.Media; + using System.Windows.Media.Imaging; + + public class GallerySampleDataItemViewModel : ViewModel + { + /// + /// Gets or sets icon + /// + public ImageSource Icon { get; set; } + /// + /// Gets or sets large icon + /// + public ImageSource IconLarge { get; set; } + /// + /// Gets or sets text + /// + public string Text { get; set; } + /// + /// Gets or sets group name + /// + public string Group { get; set; } + + /// + /// Creates new item + /// + /// Icon + /// Large Icon + /// Text + /// Group + /// Item + public static GallerySampleDataItemViewModel Create(string icon, string iconLarge, string text, string group) + { + var dataItem = new GallerySampleDataItemViewModel + { + Icon = new BitmapImage(new Uri(icon, UriKind.Relative)), + IconLarge = new BitmapImage(new Uri(iconLarge, UriKind.Relative)), + Text = text, + Group = group + }; + + return dataItem; + } + } } \ No newline at end of file diff --git a/FluentTest/ViewModels/GalleryViewModel.cs b/Fluent.Ribbon.Showcase/ViewModels/GalleryViewModel.cs similarity index 97% rename from FluentTest/ViewModels/GalleryViewModel.cs rename to Fluent.Ribbon.Showcase/ViewModels/GalleryViewModel.cs index 4361779a8..fc9c90b65 100644 --- a/FluentTest/ViewModels/GalleryViewModel.cs +++ b/Fluent.Ribbon.Showcase/ViewModels/GalleryViewModel.cs @@ -1,50 +1,50 @@ -namespace FluentTest.ViewModels -{ - using System.Collections.ObjectModel; - using System.Windows.Input; - using FluentTest.Commanding; - - public class GalleryViewModel : ViewModel - { - private ObservableCollection items; - - public GalleryViewModel() - { - this.Items = new ObservableCollection(); - this.RefreshCommand = new RelayCommand(this.Refresh); - - this.Refresh(); - } - - public ObservableCollection Items - { - get { return this.items; } - private set - { - if (Equals(value, this.items)) return; - this.items = value; - this.OnPropertyChanged("Items"); - } - } - - public ICommand RefreshCommand { get; private set; } - - public void Refresh() - { - this.Items.Clear(); - - this.Items.Add(new GalleryItemViewModel("Group 1", "1")); - this.Items.Add(new GalleryItemViewModel("Group 1", "2")); - this.Items.Add(new GalleryItemViewModel("Group 1", "3")); - this.Items.Add(new GalleryItemViewModel("Group 1", "4")); - this.Items.Add(new GalleryItemViewModel("Group 1", "5")); - this.Items.Add(new GalleryItemViewModel("Group 1", "6")); - this.Items.Add(new GalleryItemViewModel("Group 2", "10")); - this.Items.Add(new GalleryItemViewModel("Group 2", "20")); - this.Items.Add(new GalleryItemViewModel("Group 2", "30")); - this.Items.Add(new GalleryItemViewModel("Group 2", "40")); - this.Items.Add(new GalleryItemViewModel("Group 2", "50")); - this.Items.Add(new GalleryItemViewModel("Group 2", "60")); - } - } +namespace FluentTest.ViewModels +{ + using System.Collections.ObjectModel; + using System.Windows.Input; + using FluentTest.Commanding; + + public class GalleryViewModel : ViewModel + { + private ObservableCollection items; + + public GalleryViewModel() + { + this.Items = new ObservableCollection(); + this.RefreshCommand = new RelayCommand(this.Refresh); + + this.Refresh(); + } + + public ObservableCollection Items + { + get { return this.items; } + private set + { + if (Equals(value, this.items)) return; + this.items = value; + this.OnPropertyChanged("Items"); + } + } + + public ICommand RefreshCommand { get; private set; } + + public void Refresh() + { + this.Items.Clear(); + + this.Items.Add(new GalleryItemViewModel("Group 1", "1")); + this.Items.Add(new GalleryItemViewModel("Group 1", "2")); + this.Items.Add(new GalleryItemViewModel("Group 1", "3")); + this.Items.Add(new GalleryItemViewModel("Group 1", "4")); + this.Items.Add(new GalleryItemViewModel("Group 1", "5")); + this.Items.Add(new GalleryItemViewModel("Group 1", "6")); + this.Items.Add(new GalleryItemViewModel("Group 2", "10")); + this.Items.Add(new GalleryItemViewModel("Group 2", "20")); + this.Items.Add(new GalleryItemViewModel("Group 2", "30")); + this.Items.Add(new GalleryItemViewModel("Group 2", "40")); + this.Items.Add(new GalleryItemViewModel("Group 2", "50")); + this.Items.Add(new GalleryItemViewModel("Group 2", "60")); + } + } } \ No newline at end of file diff --git a/FluentTest/ViewModels/MainViewModel.cs b/Fluent.Ribbon.Showcase/ViewModels/MainViewModel.cs similarity index 96% rename from FluentTest/ViewModels/MainViewModel.cs rename to Fluent.Ribbon.Showcase/ViewModels/MainViewModel.cs index 98b17aec6..794009779 100644 --- a/FluentTest/ViewModels/MainViewModel.cs +++ b/Fluent.Ribbon.Showcase/ViewModels/MainViewModel.cs @@ -1,202 +1,202 @@ -namespace FluentTest.ViewModels -{ - using System; - using System.Diagnostics; - using System.Linq; - using System.Reflection; - using System.Windows; - using System.Windows.Input; - using Fluent; - using FluentTest.Commanding; - - public class MainViewModel : ViewModel - { - private int boundSpinnerValue; - private ColorViewModel colorViewModel; - private FontsViewModel fontsViewModel; - private GalleryViewModel galleryViewModel; - - private GallerySampleDataItemViewModel[] dataItems; - - private RelayCommand exitCommand; - private double zoom; - private ICommand testCommand; - private string[] manyItems; - private bool? isCheckedToggleButton3; - - public MainViewModel() - { - this.Title = string.Format("Fluent Ribbon Control Suite {0}", GetVersionText()); - this.Zoom = 1.0; - - this.BoundSpinnerValue = 1; - - this.ColorViewModel = new ColorViewModel(); - this.FontsViewModel = new FontsViewModel(); - this.GalleryViewModel = new GalleryViewModel(); - - this.PreviewCommand = new RelayCommand(this.Preview); - this.CancelPreviewCommand = new RelayCommand(this.CancelPreview); - } - - private static string GetVersionText() - { - var version = typeof(Ribbon).Assembly.GetName().Version; - - var attributes = typeof(Ribbon).Assembly - .GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), false) - as AssemblyInformationalVersionAttribute[]; - - var attrib = attributes.FirstOrDefault(); - - return string.Format("{0} ({1})", version, attrib.InformationalVersion); - } - - private void Preview(GalleryItem galleryItem) - { - Trace.WriteLine(string.Format("Preview: {0}", galleryItem)); - } - - private void CancelPreview(GalleryItem galleryItem) - { - Trace.WriteLine(string.Format("CancelPreview: {0}", galleryItem)); - } - - public string Title { get; private set; } - - public double Zoom - { - get { return this.zoom; } - set - { - if (value.Equals(this.zoom)) return; - this.zoom = value; - this.OnPropertyChanged("Zoom"); - } - } - - public ColorViewModel ColorViewModel - { - get { return this.colorViewModel; } - private set - { - if (Equals(value, this.colorViewModel)) return; - this.colorViewModel = value; - this.OnPropertyChanged("ColorViewModel"); - } - } - - public FontsViewModel FontsViewModel - { - get { return this.fontsViewModel; } - private set - { - if (Equals(value, this.fontsViewModel)) return; - this.fontsViewModel = value; - this.OnPropertyChanged("FontsViewModel"); - } - } - - public GalleryViewModel GalleryViewModel - { - get { return this.galleryViewModel; } - private set - { - if (Equals(value, this.galleryViewModel)) return; - this.galleryViewModel = value; - this.OnPropertyChanged("GalleryViewModel"); - } - } - - /// - /// Gets data items (uses as DataContext) - /// - public GallerySampleDataItemViewModel[] DataItems - { - get - { - return this.dataItems ?? (this.dataItems = new[] - { - GallerySampleDataItemViewModel.Create("Images\\Blue.png", "Images\\BlueLarge.png", "Blue", "Group A"), - GallerySampleDataItemViewModel.Create("Images\\Brown.png", "Images\\BrownLarge.png", "Brown", "Group A"), - GallerySampleDataItemViewModel.Create("Images\\Gray.png", "Images\\GrayLarge.png", "Gray", "Group A"), - GallerySampleDataItemViewModel.Create("Images\\Green.png", "Images\\GreenLarge.png", "Green", "Group A"), - GallerySampleDataItemViewModel.Create("Images\\Orange.png", "Images\\OrangeLarge.png", "Orange", "Group A"), - GallerySampleDataItemViewModel.Create("Images\\Pink.png", "Images\\PinkLarge.png", "Pink", "Group B"), - GallerySampleDataItemViewModel.Create("Images\\Red.png", "Images\\RedLarge.png", "Red", "Group B"), - GallerySampleDataItemViewModel.Create("Images\\Yellow.png", "Images\\YellowLarge.png", "Yellow", "Group B") - }); - } - } - - public string[] ManyItems - { - get { return this.manyItems ?? (this.manyItems = this.GenerateStrings(5000)); } - } - - public bool? IsCheckedToggleButton3 - { - get { return this.isCheckedToggleButton3; } - set - { - if (this.isCheckedToggleButton3 != value) - { - this.isCheckedToggleButton3 = value; - this.OnPropertyChanged("ToggleButton3IsChecked"); - } - } - } - - private string[] GenerateStrings(int count) - { - return Enumerable.Repeat("Test", count).ToArray(); - } - - public ICommand PreviewCommand { get; private set; } - - public ICommand CancelPreviewCommand { get; private set; } - - public int BoundSpinnerValue - { - get { return this.boundSpinnerValue; } - set - { - this.boundSpinnerValue = value; - this.OnPropertyChanged("BoundSpinnerValue"); - } - } - - #region Exit - - /// - /// Exit from the application - /// - public ICommand ExitCommand - { - get - { - if (this.exitCommand == null) - { - this.exitCommand = new RelayCommand(Application.Current.Shutdown, () => this.BoundSpinnerValue > 0); - } - - return this.exitCommand; - } - } - - #endregion - - public ICommand TestCommand - { - get - { - if (this.testCommand == null) - { - this.testCommand = new RelayCommand(() => MessageBox.Show("Test-Command")); - } - - return this.testCommand; - } - } - } +namespace FluentTest.ViewModels +{ + using System; + using System.Diagnostics; + using System.Linq; + using System.Reflection; + using System.Windows; + using System.Windows.Input; + using Fluent; + using FluentTest.Commanding; + + public class MainViewModel : ViewModel + { + private int boundSpinnerValue; + private ColorViewModel colorViewModel; + private FontsViewModel fontsViewModel; + private GalleryViewModel galleryViewModel; + + private GallerySampleDataItemViewModel[] dataItems; + + private RelayCommand exitCommand; + private double zoom; + private ICommand testCommand; + private string[] manyItems; + private bool? isCheckedToggleButton3; + + public MainViewModel() + { + this.Title = string.Format("Fluent Ribbon Control Suite {0}", GetVersionText()); + this.Zoom = 1.0; + + this.BoundSpinnerValue = 1; + + this.ColorViewModel = new ColorViewModel(); + this.FontsViewModel = new FontsViewModel(); + this.GalleryViewModel = new GalleryViewModel(); + + this.PreviewCommand = new RelayCommand(this.Preview); + this.CancelPreviewCommand = new RelayCommand(this.CancelPreview); + } + + private static string GetVersionText() + { + var version = typeof(Ribbon).Assembly.GetName().Version; + + var attributes = typeof(Ribbon).Assembly + .GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), false) + as AssemblyInformationalVersionAttribute[]; + + var attrib = attributes.FirstOrDefault(); + + return string.Format("{0} ({1})", version, attrib.InformationalVersion); + } + + private void Preview(GalleryItem galleryItem) + { + Trace.WriteLine(string.Format("Preview: {0}", galleryItem)); + } + + private void CancelPreview(GalleryItem galleryItem) + { + Trace.WriteLine(string.Format("CancelPreview: {0}", galleryItem)); + } + + public string Title { get; private set; } + + public double Zoom + { + get { return this.zoom; } + set + { + if (value.Equals(this.zoom)) return; + this.zoom = value; + this.OnPropertyChanged("Zoom"); + } + } + + public ColorViewModel ColorViewModel + { + get { return this.colorViewModel; } + private set + { + if (Equals(value, this.colorViewModel)) return; + this.colorViewModel = value; + this.OnPropertyChanged("ColorViewModel"); + } + } + + public FontsViewModel FontsViewModel + { + get { return this.fontsViewModel; } + private set + { + if (Equals(value, this.fontsViewModel)) return; + this.fontsViewModel = value; + this.OnPropertyChanged("FontsViewModel"); + } + } + + public GalleryViewModel GalleryViewModel + { + get { return this.galleryViewModel; } + private set + { + if (Equals(value, this.galleryViewModel)) return; + this.galleryViewModel = value; + this.OnPropertyChanged("GalleryViewModel"); + } + } + + /// + /// Gets data items (uses as DataContext) + /// + public GallerySampleDataItemViewModel[] DataItems + { + get + { + return this.dataItems ?? (this.dataItems = new[] + { + GallerySampleDataItemViewModel.Create("Images\\Blue.png", "Images\\BlueLarge.png", "Blue", "Group A"), + GallerySampleDataItemViewModel.Create("Images\\Brown.png", "Images\\BrownLarge.png", "Brown", "Group A"), + GallerySampleDataItemViewModel.Create("Images\\Gray.png", "Images\\GrayLarge.png", "Gray", "Group A"), + GallerySampleDataItemViewModel.Create("Images\\Green.png", "Images\\GreenLarge.png", "Green", "Group A"), + GallerySampleDataItemViewModel.Create("Images\\Orange.png", "Images\\OrangeLarge.png", "Orange", "Group A"), + GallerySampleDataItemViewModel.Create("Images\\Pink.png", "Images\\PinkLarge.png", "Pink", "Group B"), + GallerySampleDataItemViewModel.Create("Images\\Red.png", "Images\\RedLarge.png", "Red", "Group B"), + GallerySampleDataItemViewModel.Create("Images\\Yellow.png", "Images\\YellowLarge.png", "Yellow", "Group B") + }); + } + } + + public string[] ManyItems + { + get { return this.manyItems ?? (this.manyItems = this.GenerateStrings(5000)); } + } + + public bool? IsCheckedToggleButton3 + { + get { return this.isCheckedToggleButton3; } + set + { + if (this.isCheckedToggleButton3 != value) + { + this.isCheckedToggleButton3 = value; + this.OnPropertyChanged("ToggleButton3IsChecked"); + } + } + } + + private string[] GenerateStrings(int count) + { + return Enumerable.Repeat("Test", count).ToArray(); + } + + public ICommand PreviewCommand { get; private set; } + + public ICommand CancelPreviewCommand { get; private set; } + + public int BoundSpinnerValue + { + get { return this.boundSpinnerValue; } + set + { + this.boundSpinnerValue = value; + this.OnPropertyChanged("BoundSpinnerValue"); + } + } + + #region Exit + + /// + /// Exit from the application + /// + public ICommand ExitCommand + { + get + { + if (this.exitCommand == null) + { + this.exitCommand = new RelayCommand(Application.Current.Shutdown, () => this.BoundSpinnerValue > 0); + } + + return this.exitCommand; + } + } + + #endregion + + public ICommand TestCommand + { + get + { + if (this.testCommand == null) + { + this.testCommand = new RelayCommand(() => MessageBox.Show("Test-Command")); + } + + return this.testCommand; + } + } + } } \ No newline at end of file diff --git a/FluentTest/ViewModels/ViewModel.cs b/Fluent.Ribbon.Showcase/ViewModels/ViewModel.cs similarity index 96% rename from FluentTest/ViewModels/ViewModel.cs rename to Fluent.Ribbon.Showcase/ViewModels/ViewModel.cs index d8b41ada0..d6c113f4a 100644 --- a/FluentTest/ViewModels/ViewModel.cs +++ b/Fluent.Ribbon.Showcase/ViewModels/ViewModel.cs @@ -1,19 +1,19 @@ -namespace FluentTest.ViewModels -{ - using System.ComponentModel; - using JetBrains.Annotations; - - public class ViewModel : INotifyPropertyChanged - { - public event PropertyChangedEventHandler PropertyChanged = delegate { }; - - [NotifyPropertyChangedInvocator] - protected void OnPropertyChanged(string propertyName) - { - if (this.PropertyChanged != null) - { - this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); - } - } - } +namespace FluentTest.ViewModels +{ + using System.ComponentModel; + using JetBrains.Annotations; + + public class ViewModel : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged = delegate { }; + + [NotifyPropertyChangedInvocator] + protected void OnPropertyChanged(string propertyName) + { + if (this.PropertyChanged != null) + { + this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); + } + } + } } \ No newline at end of file diff --git a/FluentTest/app.manifest b/Fluent.Ribbon.Showcase/app.manifest similarity index 100% rename from FluentTest/app.manifest rename to Fluent.Ribbon.Showcase/app.manifest diff --git a/FluentTest/packages.config b/Fluent.Ribbon.Showcase/packages.config similarity index 100% rename from FluentTest/packages.config rename to Fluent.Ribbon.Showcase/packages.config diff --git a/Fluent.Ribbon.sln b/Fluent.Ribbon.sln index 2e4c032aa..602843f5d 100644 --- a/Fluent.Ribbon.sln +++ b/Fluent.Ribbon.sln @@ -1,9 +1,9 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.31101.0 +# Visual Studio 14 +VisualStudioVersion = 14.0.24720.0 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FluentTest dotNET 4.5", "FluentTest\FluentTest dotNET 4.5.csproj", "{2C2A076B-E626-4A07-9D6E-4AEE3FEC41B6}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fluent.Ribbon.Showcase.NET 4.5", "Fluent.Ribbon.Showcase\Fluent.Ribbon.Showcase.NET 4.5.csproj", "{2C2A076B-E626-4A07-9D6E-4AEE3FEC41B6}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fluent dotNET 4.5", "Fluent\Fluent dotNET 4.5.csproj", "{4C92FCF4-3561-499F-BC5B-F2F089863047}" EndProject @@ -20,7 +20,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fluent dotNET 4.0", "Fluent {4C92FCF4-3561-499F-BC5B-F2F089863047} = {4C92FCF4-3561-499F-BC5B-F2F089863047} EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FluentTest dotNET 4.0", "FluentTest\FluentTest dotNET 4.0.csproj", "{2A893D50-BBC4-4482-ADAA-D52B517E8632}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fluent.Ribbon.Showcase.NET 4.0", "Fluent.Ribbon.Showcase\Fluent.Ribbon.Showcase.NET 4.0.csproj", "{2A893D50-BBC4-4482-ADAA-D52B517E8632}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Doc", "Doc", "{1318E3D3-5156-45FE-9828-464A2D0235E0}" ProjectSection(SolutionItems) = preProject From 2af10b2a2718af414af5bf121b797704413f68a6 Mon Sep 17 00:00:00 2001 From: Bastian Schmidt Date: Sun, 13 Dec 2015 19:22:30 +0100 Subject: [PATCH 015/186] Renaming projects from "Fluent" to "Fluent.Ribbon" --- .../Fluent.Ribbon.Showcase.NET 4.0.csproj | 6 +- .../Fluent.Ribbon.Showcase.NET 4.5.csproj | 4 +- Fluent.Ribbon.sln | 4 +- .../Adorners/KeyTipAdorner.cs | 2204 ++--- .../AttachedProperties/RibbonProperties.cs | 336 +- {Fluent => Fluent.Ribbon}/BackstageButton.cs | 296 +- .../Controls/ApplicationMenu.cs | 240 +- .../Controls/Backstage.cs | 1412 +-- .../Controls/BackstageAdorner.cs | 226 +- .../Controls/BackstageTabControl.cs | 882 +- .../Controls/BackstageTabItem.cs | 478 +- {Fluent => Fluent.Ribbon}/Controls/Button.cs | 566 +- .../Controls/CheckBox.cs | 466 +- .../Controls/ColorGallery.cs | 2146 ++--- .../Controls/ComboBox.cs | 1940 ++--- .../Controls/ContextMenu.cs | 310 +- .../Controls/DropDownButton.cs | 1822 ++-- {Fluent => Fluent.Ribbon}/Controls/Gallery.cs | 1064 +-- .../Controls/GalleryGroupContainer.cs | 610 +- .../Controls/GalleryGroupFilter.cs | 86 +- .../Controls/GalleryGroupIcon.cs | 90 +- .../Controls/GalleryItem.cs | 930 +- .../Controls/GalleryItemPlaceholder.cs | 192 +- .../Controls/GalleryPanel.cs | 1244 +-- .../Controls/GroupSeparatorMenuItem.cs | 78 +- .../Controls/InRibbonGallery.cs | 3032 +++---- {Fluent => Fluent.Ribbon}/Controls/KeyTip.cs | 476 +- .../Controls/MenuItem.cs | 1554 ++-- .../Controls/QuickAccessMenuItem.cs | 572 +- .../Controls/QuickAccessToolBar.cs | 1232 +-- .../Controls/RadioButton.cs | 468 +- {Fluent => Fluent.Ribbon}/Controls/Ribbon.cs | 4456 +++++----- .../RibbonContextualGroupsContainer.cs | 276 +- .../Controls/RibbonContextualTabGroup.cs | 732 +- .../Controls/RibbonControl.cs | 1018 +-- .../Controls/RibbonGroupBox.cs | 2242 ++--- .../Controls/RibbonGroupsContainer.cs | 1198 +-- .../Controls/RibbonItemsControl.cs | 352 +- .../Controls/RibbonMenu.cs | 194 +- .../Controls/RibbonScrollViewer.cs | 52 +- .../Controls/RibbonTabControl.cs | 1640 ++-- .../Controls/RibbonTabItem.cs | 1488 ++-- .../Controls/RibbonTabsContainer.cs | 1510 ++-- .../Controls/RibbonTitleBar.cs | 834 +- .../Controls/RibbonToolBar.cs | 1162 +-- .../RibbonToolBarControlDefinition.cs | 282 +- .../Controls/RibbonToolBarControlGroup.cs | 150 +- .../RibbonToolBarControlGroupDefinition.cs | 124 +- .../Controls/RibbonToolBarLayoutDefinition.cs | 188 +- .../Controls/RibbonToolBarRow.cs | 92 +- .../Controls/RibbonWindow.cs | 872 +- .../Controls/ScreenTip.cs | 856 +- .../Controls/SeparatorTabItem.cs | 122 +- {Fluent => Fluent.Ribbon}/Controls/Spinner.cs | 1102 +-- .../Controls/SplitButton.cs | 1042 +-- .../Controls/StatusBar.cs | 706 +- .../Controls/StatusBarItem.cs | 318 +- .../Controls/StatusBarMenuItem.cs | 112 +- .../Controls/StatusBarPanel.cs | 346 +- {Fluent => Fluent.Ribbon}/Controls/TextBox.cs | 1474 ++-- .../Controls/ToggleButton.cs | 632 +- .../Controls/TwoLineLabel.cs | 452 +- ...ationMenuRightContentExtractorConverter.cs | 92 +- .../ColorToSolidColorBrushConverter.cs | 0 .../Converters/IconConverter.cs | 262 +- .../Converters/InvertNumericConverter.cs | 150 +- .../Converters/ObjectToImageConverter.cs | 382 +- .../Converters/SizeDefinitionConverter.cs | 70 +- .../Converters/StaticConverters.cs | 54 +- .../Converters/ThicknessConverter.cs | 106 +- .../Documents/Features.xlsx | Bin ...uent Ribbon Control Suite Walkthrough.docx | Bin .../Effects/GrayscaleEffect.cs | 154 +- .../Enumerations/RibbonControlSize.cs | 44 +- .../Enumerations/RibbonGroupBoxState.cs | 64 +- .../Extensibility/IRibbonSizeChangedSink.cs | 28 +- .../Extensions/DispatcherExtensions.cs | 106 +- .../Fluent dotNET 4.0.csproj.DotSettings | 0 .../Fluent dotNET 4.5.csproj.DotSettings | 0 .../Fluent.Ribbon.NET 4.0.csproj | 1558 ++-- .../Fluent.Ribbon.NET 4.0.csproj.DotSettings | 2 + .../Fluent.Ribbon.NET 4.5.csproj | 1558 ++-- .../Fluent.Ribbon.NET 4.5.csproj.DotSettings | 2 + .../Helpers/FrameworkHelper.cs | 102 +- .../Helpers/ToggleButtonHelper.cs | 290 +- {Fluent => Fluent.Ribbon}/IDropDownControl.cs | 48 +- {Fluent => Fluent.Ribbon}/IHeaderedControl.cs | 0 {Fluent => Fluent.Ribbon}/IKeyTipedControl.cs | 44 +- {Fluent => Fluent.Ribbon}/IRibbonControl.cs | 44 +- .../IScalableRibbonControl.cs | 48 +- {Fluent => Fluent.Ribbon}/IToggleButton.cs | 54 +- .../Images/DefaultSmallIcon.png | Bin .../Internal/CommandHelper.cs | 114 +- .../Internal/DoubleUtil.cs | 76 +- .../Internal/InvokeCommand.cs | 132 +- .../Internal/ItemContainerGeneratorAction.cs | 130 +- .../Internal/ItemsControlHelper.cs | 0 .../Internal/NativeMethods.cs | 270 +- .../Internal/UIHelper.cs | 118 +- .../Metro/Behaviours/StylizedBehaviors.cs | 252 +- .../Behaviours/StylizedBehaviorsCollection.cs | 42 +- .../Metro/Controls/WindowCommands.cs | 550 +- .../Metro/MetroColors.cs | 28 +- .../Metro/Native/ABEdge.cs | 18 +- .../Metro/Native/ABMsg.cs | 32 +- .../Metro/Native/APPBARDATA.cs | 30 +- .../Metro/Native/CREATESTRUCT.cs | 44 +- .../Metro/Native/Constants.cs | 90 +- .../Metro/Native/MARGINS.cs | 26 +- .../Metro/Native/MINMAXINFO.cs | 38 +- .../Metro/Native/MONITORINFO.cs | 40 +- .../Metro/Native/POINT.cs | 124 +- .../Metro/Native/RECT.cs | 180 +- .../Metro/Native/UnsafeNativeMethods.cs | 428 +- .../Metro/Native/WINDOWPLACEMENT.cs | 58 +- .../Properties/AssemblyInfo.cs | 24 +- .../Properties/FluentStrongName.snk | Bin {Fluent => Fluent.Ribbon}/RibbonCommands.cs | 38 +- .../RibbonControlSizeDefinition.cs | 280 +- .../RibbonGroupBoxPanel.cs | 38 +- .../RibbonLocalization.cs | 4434 +++++----- .../RibbonToolBarTray.cs | 806 +- .../Services/ContextMenuService.cs | 92 +- .../Services/KeyTipService.cs | 836 +- .../Services/PopupService.cs | 742 +- .../Services/ToolTipService.cs | 52 +- {Fluent => Fluent.Ribbon}/Themes/Generic.xaml | 0 .../Themes/Generic/Common.xaml | 0 .../Generic/Controls/EmptyFocusStyle.xaml | 0 .../Controls/GalleryGroupContainer.xaml | 0 .../Generic/Controls/MenuSeparator.xaml | 0 .../Themes/Generic/Controls/RadioButton.xaml | 0 .../Themes/Generic/Controls/RibbonMenu.xaml | 0 .../Generic/Controls/RibbonScrollViewer.xaml | 0 .../Generic/Controls/RibbonTextBox.xaml | 0 .../Generic/Controls/RibbonTitleBar.xaml | 0 .../Generic/Controls/RibbonToolBar.xaml | 0 .../Controls/RibbonToolBarControlGroup.xaml | 0 .../Generic/Controls/SeparatorTabItem.xaml | 0 .../Themes/Generic/Controls/TextBox.xaml | 0 .../Themes/Generic/Controls/TwoLineLabel.xaml | 0 .../Themes/Office2010/Black.xaml | 10 +- .../Themes/Office2010/Blue.xaml | 10 +- .../Themes/Office2010/Colors/Colors.xaml | 796 +- .../Themes/Office2010/Colors/ColorsBlack.xaml | 972 +-- .../Themes/Office2010/Colors/ColorsBlue.xaml | 966 +-- .../Office2010/Colors/ColorsSilver.xaml | 948 +- .../Office2010/Controls/ApplicationMenu.xaml | 802 +- .../Controls/ApplicationMenuItem.xaml | 3428 ++++---- .../Themes/Office2010/Controls/Backstage.xaml | 588 +- .../Controls/BackstageControls.xaml | 3154 +++---- .../Controls/BackstageTabControl.xaml | 566 +- .../Office2010/Controls/BackstageTabItem.xaml | 254 +- .../Themes/Office2010/Controls/Button.xaml | 450 +- .../Themes/Office2010/Controls/CheckBox.xaml | 378 +- .../Office2010/Controls/ColorGallery.xaml | 860 +- .../Themes/Office2010/Controls/ComboBox.xaml | 1344 +-- .../Office2010/Controls/ComboBoxItem.xaml | 246 +- .../Office2010/Controls/DropDownButton.xaml | 712 +- .../Themes/Office2010/Controls/Gallery.xaml | 406 +- .../Office2010/Controls/GalleryItem.xaml | 174 +- .../Office2010/Controls/InRibbonGallery.xaml | 1422 +-- .../Themes/Office2010/Controls/KeyTip.xaml | 76 +- .../Themes/Office2010/Controls/Menu.xaml | 470 +- .../Themes/Office2010/Controls/MenuItem.xaml | 1900 ++-- .../Controls/QuickAccessToolbar.xaml | 890 +- .../Themes/Office2010/Controls/Ribbon.xaml | 280 +- .../Controls/RibbonContextualTabGroup.xaml | 244 +- .../Office2010/Controls/RibbonGroupBox.xaml | 1628 ++-- .../Office2010/Controls/RibbonSeparator.xaml | 48 +- .../Office2010/Controls/RibbonStatusBar.xaml | 440 +- .../Office2010/Controls/RibbonTabControl.xaml | 1246 +-- .../Office2010/Controls/RibbonTabItem.xaml | 1034 +-- .../Themes/Office2010/Controls/ScreenTip.xaml | 892 +- .../Themes/Office2010/Controls/ScrollBar.xaml | 1272 +-- .../Office2010/Controls/ScrollBarWhite.xaml | 1150 +-- .../Themes/Office2010/Controls/Slider.xaml | 686 +- .../Themes/Office2010/Controls/Spinner.xaml | 620 +- .../Office2010/Controls/SplitButton.xaml | 1476 ++-- .../Themes/Office2010/Controls/StatusBar.xaml | 130 +- .../Office2010/Controls/ToggleButton.xaml | 490 +- .../Themes/Office2010/Effects/Grayscale.fx | 62 +- .../Themes/Office2010/Effects/Grayscale.ps | Bin .../Themes/Office2010/Generic.txt | 0 Fluent.Ribbon/Themes/Office2010/Generic.xaml | 7705 +++++++++++++++++ .../Office2010/Images/ApplicationMenu.png | Bin .../Office2010/Images/BackstageCorner.png | Bin .../Office2010/Images/CaptionButtonClose.png | Bin .../Images/CaptionButtonMaximize.png | Bin .../Images/CaptionButtonMinimize.png | Bin .../Images/CaptionButtonNormalize.png | Bin .../Themes/Office2010/Images/Checked.png | Bin .../Themes/Office2010/Images/Copy.png | Bin .../Themes/Office2010/Images/Cut.png | Bin .../Office2010/Images/DialogLauncher.png | Bin .../Themes/Office2010/Images/Help.png | Bin .../Themes/Office2010/Images/Images.xaml | 56 +- .../Themes/Office2010/Images/MoreColors.png | Bin .../Themes/Office2010/Images/Paste.png | Bin .../Images/QuickAccessToolbarDropDown.png | Bin .../Images/QuickAccessToolbarExtender.png | Bin .../Themes/Office2010/Images/ResizeGrip.png | Bin .../Office2010/Images/RibbonCollapse.png | Bin .../Themes/Office2010/Images/RibbonExpand.png | Bin .../Themes/Office2010/Images/RibbonPin.png | Bin .../Themes/Office2010/Images/Warning.png | Bin .../Themes/Office2010/RibbonWindow.xaml | 1522 ++-- .../Themes/Office2010/Silver.xaml | 10 +- .../Themes/Office2013/Colors/Colors.xaml | 850 +- .../Themes/Office2013/Colors/ColorsWhite.xaml | 598 +- .../Office2013/Controls/ApplicationMenu.xaml | 344 +- .../Themes/Office2013/Controls/Backstage.xaml | 172 +- .../Controls/BackstageControls.xaml | 2574 +++--- .../Controls/BackstageTabControl.xaml | 570 +- .../Office2013/Controls/BackstageTabItem.xaml | 176 +- .../Themes/Office2013/Controls/Button.xaml | 498 +- .../Themes/Office2013/Controls/CheckBox.xaml | 480 +- .../Themes/Office2013/Controls/ComboBox.xaml | 1278 +-- .../Office2013/Controls/ComboBoxItem.xaml | 266 +- .../Office2013/Controls/DropDownButton.xaml | 606 +- .../Themes/Office2013/Controls/Gallery.xaml | 408 +- .../Office2013/Controls/GalleryItem.xaml | 140 +- .../Office2013/Controls/InRibbonGallery.xaml | 1312 +-- .../Themes/Office2013/Controls/KeyTip.xaml | 70 +- .../Themes/Office2013/Controls/Menu.xaml | 416 +- .../Themes/Office2013/Controls/MenuItem.xaml | 1750 ++-- .../Controls/QuickAccessToolbar.xaml | 822 +- .../Themes/Office2013/Controls/Ribbon.xaml | 240 +- .../Controls/RibbonContextualTabGroup.xaml | 132 +- .../Office2013/Controls/RibbonGroupBox.xaml | 1588 ++-- .../Office2013/Controls/RibbonSeparator.xaml | 46 +- .../Office2013/Controls/RibbonStatusBar.xaml | 414 +- .../Office2013/Controls/RibbonTabControl.xaml | 834 +- .../Office2013/Controls/RibbonTabItem.xaml | 696 +- .../Themes/Office2013/Controls/ScreenTip.xaml | 474 +- .../Themes/Office2013/Controls/ScrollBar.xaml | 922 +- .../Themes/Office2013/Controls/Slider.xaml | 526 +- .../Themes/Office2013/Controls/Spinner.xaml | 544 +- .../Office2013/Controls/SplitButton.xaml | 938 +- .../Themes/Office2013/Controls/StatusBar.xaml | 124 +- .../Office2013/Controls/ToggleButton.xaml | 406 +- .../Themes/Office2013/Generic.txt | 100 +- Fluent.Ribbon/Themes/Office2013/Generic.xaml | 6295 ++++++++++++++ .../Themes/Office2013/Images/Checked.png | Bin .../Themes/Office2013/Images/Copy.png | Bin .../Themes/Office2013/Images/Cut.png | Bin .../Themes/Office2013/Images/Help.png | Bin .../Themes/Office2013/Images/Images.xaml | 56 +- .../Themes/Office2013/Images/MoreColors.png | Bin .../Themes/Office2013/Images/Paste.png | Bin .../Themes/Office2013/Images/Warning.png | Bin .../Themes/Office2013/RibbonWindow.xaml | 614 +- .../Themes/Windows8/Colors/Colors.xaml | 0 .../Themes/Windows8/Colors/ColorsSilver.xaml | 0 .../Windows8/Controls/ApplicationMenu.xaml | 0 .../Controls/ApplicationMenuItem.xaml | 0 .../Themes/Windows8/Controls/Backstage.xaml | 0 .../Windows8/Controls/BackstageControls.xaml | 0 .../Controls/BackstageTabControl.xaml | 0 .../Windows8/Controls/BackstageTabItem.xaml | 0 .../Themes/Windows8/Controls/Button.xaml | 0 .../Themes/Windows8/Controls/CheckBox.xaml | 0 .../Windows8/Controls/ColorGallery.xaml | 0 .../Themes/Windows8/Controls/ComboBox.xaml | 0 .../Windows8/Controls/ComboBoxItem.xaml | 0 .../Windows8/Controls/DropDownButton.xaml | 0 .../Themes/Windows8/Controls/Gallery.xaml | 0 .../Themes/Windows8/Controls/GalleryItem.xaml | 0 .../Windows8/Controls/InRibbonGallery.xaml | 0 .../Themes/Windows8/Controls/Menu.xaml | 0 .../Themes/Windows8/Controls/MenuItem.xaml | 0 .../Windows8/Controls/QuickAccessToolbar.xaml | 0 .../Themes/Windows8/Controls/Ribbon.xaml | 0 .../Controls/RibbonContextualTabGroup.xaml | 0 .../Windows8/Controls/RibbonGroupBox.xaml | 0 .../Windows8/Controls/RibbonSeparator.xaml | 0 .../Windows8/Controls/RibbonStatusBar.xaml | 0 .../Windows8/Controls/RibbonTabControl.xaml | 0 .../Windows8/Controls/RibbonTabItem.xaml | 0 .../Themes/Windows8/Controls/ScrollBar.xaml | 0 .../Themes/Windows8/Controls/Slider.xaml | 0 .../Themes/Windows8/Controls/Spinner.xaml | 0 .../Themes/Windows8/Controls/SplitButton.xaml | 0 .../Themes/Windows8/Controls/StatusBar.xaml | 0 .../Windows8/Controls/ToggleButton.xaml | 0 .../Themes/Windows8/Generic.txt | 0 Fluent.Ribbon/Themes/Windows8/Generic.xaml | 6380 ++++++++++++++ .../Themes/Windows8/Images/Images.xaml | 0 .../Themes/Windows8/RibbonWindow.xaml | 0 .../Themes/Windows8/Silver.xaml | 0 {Fluent => Fluent.Ribbon}/packages.config | 0 Fluent/Themes/XamlCombine.exe | Bin 12288 -> 0 bytes 292 files changed, 82135 insertions(+), 61751 deletions(-) rename {Fluent => Fluent.Ribbon}/Adorners/KeyTipAdorner.cs (97%) rename {Fluent => Fluent.Ribbon}/AttachedProperties/RibbonProperties.cs (97%) rename {Fluent => Fluent.Ribbon}/BackstageButton.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/ApplicationMenu.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/Backstage.cs (96%) rename {Fluent => Fluent.Ribbon}/Controls/BackstageAdorner.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/BackstageTabControl.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/BackstageTabItem.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/Button.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/CheckBox.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/ColorGallery.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/ComboBox.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/ContextMenu.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/DropDownButton.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/Gallery.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/GalleryGroupContainer.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/GalleryGroupFilter.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/GalleryGroupIcon.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/GalleryItem.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/GalleryItemPlaceholder.cs (96%) rename {Fluent => Fluent.Ribbon}/Controls/GalleryPanel.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/GroupSeparatorMenuItem.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/InRibbonGallery.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/KeyTip.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/MenuItem.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/QuickAccessMenuItem.cs (96%) rename {Fluent => Fluent.Ribbon}/Controls/QuickAccessToolBar.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/RadioButton.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/Ribbon.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/RibbonContextualGroupsContainer.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/RibbonContextualTabGroup.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/RibbonControl.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/RibbonGroupBox.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/RibbonGroupsContainer.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/RibbonItemsControl.cs (96%) rename {Fluent => Fluent.Ribbon}/Controls/RibbonMenu.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/RibbonScrollViewer.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/RibbonTabControl.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/RibbonTabItem.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/RibbonTabsContainer.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/RibbonTitleBar.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/RibbonToolBar.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/RibbonToolBarControlDefinition.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/RibbonToolBarControlGroup.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/RibbonToolBarControlGroupDefinition.cs (96%) rename {Fluent => Fluent.Ribbon}/Controls/RibbonToolBarLayoutDefinition.cs (96%) rename {Fluent => Fluent.Ribbon}/Controls/RibbonToolBarRow.cs (96%) rename {Fluent => Fluent.Ribbon}/Controls/RibbonWindow.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/ScreenTip.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/SeparatorTabItem.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/Spinner.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/SplitButton.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/StatusBar.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/StatusBarItem.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/StatusBarMenuItem.cs (96%) rename {Fluent => Fluent.Ribbon}/Controls/StatusBarPanel.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/TextBox.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/ToggleButton.cs (97%) rename {Fluent => Fluent.Ribbon}/Controls/TwoLineLabel.cs (97%) rename {Fluent => Fluent.Ribbon}/Converters/ApplicationMenuRightContentExtractorConverter.cs (97%) rename {Fluent => Fluent.Ribbon}/Converters/ColorToSolidColorBrushConverter.cs (100%) rename {Fluent => Fluent.Ribbon}/Converters/IconConverter.cs (96%) rename {Fluent => Fluent.Ribbon}/Converters/InvertNumericConverter.cs (97%) rename {Fluent => Fluent.Ribbon}/Converters/ObjectToImageConverter.cs (97%) rename {Fluent => Fluent.Ribbon}/Converters/SizeDefinitionConverter.cs (98%) rename {Fluent => Fluent.Ribbon}/Converters/StaticConverters.cs (97%) rename {Fluent => Fluent.Ribbon}/Converters/ThicknessConverter.cs (98%) rename {Fluent => Fluent.Ribbon}/Documents/Features.xlsx (100%) rename {Fluent => Fluent.Ribbon}/Documents/Fluent Ribbon Control Suite Walkthrough.docx (100%) rename {Fluent => Fluent.Ribbon}/Effects/GrayscaleEffect.cs (96%) rename {Fluent => Fluent.Ribbon}/Enumerations/RibbonControlSize.cs (95%) rename {Fluent => Fluent.Ribbon}/Enumerations/RibbonGroupBoxState.cs (96%) rename {Fluent => Fluent.Ribbon}/Extensibility/IRibbonSizeChangedSink.cs (97%) rename {Fluent => Fluent.Ribbon}/Extensions/DispatcherExtensions.cs (96%) rename {Fluent => Fluent.Ribbon}/Fluent dotNET 4.0.csproj.DotSettings (100%) rename {Fluent => Fluent.Ribbon}/Fluent dotNET 4.5.csproj.DotSettings (100%) rename Fluent/Fluent dotNET 4.0.csproj => Fluent.Ribbon/Fluent.Ribbon.NET 4.0.csproj (97%) create mode 100644 Fluent.Ribbon/Fluent.Ribbon.NET 4.0.csproj.DotSettings rename Fluent/Fluent dotNET 4.5.csproj => Fluent.Ribbon/Fluent.Ribbon.NET 4.5.csproj (97%) create mode 100644 Fluent.Ribbon/Fluent.Ribbon.NET 4.5.csproj.DotSettings rename {Fluent => Fluent.Ribbon}/Helpers/FrameworkHelper.cs (97%) rename {Fluent => Fluent.Ribbon}/Helpers/ToggleButtonHelper.cs (97%) rename {Fluent => Fluent.Ribbon}/IDropDownControl.cs (96%) rename {Fluent => Fluent.Ribbon}/IHeaderedControl.cs (100%) rename {Fluent => Fluent.Ribbon}/IKeyTipedControl.cs (96%) rename {Fluent => Fluent.Ribbon}/IRibbonControl.cs (96%) rename {Fluent => Fluent.Ribbon}/IScalableRibbonControl.cs (95%) rename {Fluent => Fluent.Ribbon}/IToggleButton.cs (97%) rename {Fluent => Fluent.Ribbon}/Images/DefaultSmallIcon.png (100%) rename {Fluent => Fluent.Ribbon}/Internal/CommandHelper.cs (96%) rename {Fluent => Fluent.Ribbon}/Internal/DoubleUtil.cs (97%) rename {Fluent => Fluent.Ribbon}/Internal/InvokeCommand.cs (97%) rename {Fluent => Fluent.Ribbon}/Internal/ItemContainerGeneratorAction.cs (97%) rename {Fluent => Fluent.Ribbon}/Internal/ItemsControlHelper.cs (100%) rename {Fluent => Fluent.Ribbon}/Internal/NativeMethods.cs (98%) rename {Fluent => Fluent.Ribbon}/Internal/UIHelper.cs (97%) rename {Fluent => Fluent.Ribbon}/Metro/Behaviours/StylizedBehaviors.cs (97%) rename {Fluent => Fluent.Ribbon}/Metro/Behaviours/StylizedBehaviorsCollection.cs (96%) rename {Fluent => Fluent.Ribbon}/Metro/Controls/WindowCommands.cs (97%) rename {Fluent => Fluent.Ribbon}/Metro/MetroColors.cs (97%) rename {Fluent => Fluent.Ribbon}/Metro/Native/ABEdge.cs (94%) rename {Fluent => Fluent.Ribbon}/Metro/Native/ABMsg.cs (95%) rename {Fluent => Fluent.Ribbon}/Metro/Native/APPBARDATA.cs (95%) rename {Fluent => Fluent.Ribbon}/Metro/Native/CREATESTRUCT.cs (96%) rename {Fluent => Fluent.Ribbon}/Metro/Native/Constants.cs (97%) rename {Fluent => Fluent.Ribbon}/Metro/Native/MARGINS.cs (95%) rename {Fluent => Fluent.Ribbon}/Metro/Native/MINMAXINFO.cs (96%) rename {Fluent => Fluent.Ribbon}/Metro/Native/MONITORINFO.cs (96%) rename {Fluent => Fluent.Ribbon}/Metro/Native/POINT.cs (95%) rename {Fluent => Fluent.Ribbon}/Metro/Native/RECT.cs (96%) rename {Fluent => Fluent.Ribbon}/Metro/Native/UnsafeNativeMethods.cs (98%) rename {Fluent => Fluent.Ribbon}/Metro/Native/WINDOWPLACEMENT.cs (96%) rename {Fluent => Fluent.Ribbon}/Properties/AssemblyInfo.cs (97%) rename {Fluent => Fluent.Ribbon}/Properties/FluentStrongName.snk (100%) rename {Fluent => Fluent.Ribbon}/RibbonCommands.cs (96%) rename {Fluent => Fluent.Ribbon}/RibbonControlSizeDefinition.cs (97%) rename {Fluent => Fluent.Ribbon}/RibbonGroupBoxPanel.cs (96%) rename {Fluent => Fluent.Ribbon}/RibbonLocalization.cs (98%) rename {Fluent => Fluent.Ribbon}/RibbonToolBarTray.cs (97%) rename {Fluent => Fluent.Ribbon}/Services/ContextMenuService.cs (97%) rename {Fluent => Fluent.Ribbon}/Services/KeyTipService.cs (96%) rename {Fluent => Fluent.Ribbon}/Services/PopupService.cs (97%) rename {Fluent => Fluent.Ribbon}/Services/ToolTipService.cs (97%) rename {Fluent => Fluent.Ribbon}/Themes/Generic.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Generic/Common.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Generic/Controls/EmptyFocusStyle.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Generic/Controls/GalleryGroupContainer.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Generic/Controls/MenuSeparator.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Generic/Controls/RadioButton.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Generic/Controls/RibbonMenu.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Generic/Controls/RibbonScrollViewer.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Generic/Controls/RibbonTextBox.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Generic/Controls/RibbonTitleBar.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Generic/Controls/RibbonToolBar.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Generic/Controls/RibbonToolBarControlGroup.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Generic/Controls/SeparatorTabItem.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Generic/Controls/TextBox.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Generic/Controls/TwoLineLabel.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Black.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Blue.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Colors/Colors.xaml (97%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Colors/ColorsBlack.xaml (97%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Colors/ColorsBlue.xaml (97%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Colors/ColorsSilver.xaml (97%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Controls/ApplicationMenu.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Controls/ApplicationMenuItem.xaml (97%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Controls/Backstage.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Controls/BackstageControls.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Controls/BackstageTabControl.xaml (97%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Controls/BackstageTabItem.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Controls/Button.xaml (97%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Controls/CheckBox.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Controls/ColorGallery.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Controls/ComboBox.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Controls/ComboBoxItem.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Controls/DropDownButton.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Controls/Gallery.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Controls/GalleryItem.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Controls/InRibbonGallery.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Controls/KeyTip.xaml (97%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Controls/Menu.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Controls/MenuItem.xaml (97%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Controls/QuickAccessToolbar.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Controls/Ribbon.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Controls/RibbonContextualTabGroup.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Controls/RibbonGroupBox.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Controls/RibbonSeparator.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Controls/RibbonStatusBar.xaml (97%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Controls/RibbonTabControl.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Controls/RibbonTabItem.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Controls/ScreenTip.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Controls/ScrollBar.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Controls/ScrollBarWhite.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Controls/Slider.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Controls/Spinner.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Controls/SplitButton.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Controls/StatusBar.xaml (97%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Controls/ToggleButton.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Effects/Grayscale.fx (97%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Effects/Grayscale.ps (100%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Generic.txt (100%) create mode 100644 Fluent.Ribbon/Themes/Office2010/Generic.xaml rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Images/ApplicationMenu.png (100%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Images/BackstageCorner.png (100%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Images/CaptionButtonClose.png (100%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Images/CaptionButtonMaximize.png (100%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Images/CaptionButtonMinimize.png (100%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Images/CaptionButtonNormalize.png (100%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Images/Checked.png (100%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Images/Copy.png (100%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Images/Cut.png (100%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Images/DialogLauncher.png (100%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Images/Help.png (100%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Images/Images.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Images/MoreColors.png (100%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Images/Paste.png (100%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Images/QuickAccessToolbarDropDown.png (100%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Images/QuickAccessToolbarExtender.png (100%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Images/ResizeGrip.png (100%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Images/RibbonCollapse.png (100%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Images/RibbonExpand.png (100%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Images/RibbonPin.png (100%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Images/Warning.png (100%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/RibbonWindow.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2010/Silver.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2013/Colors/Colors.xaml (97%) rename {Fluent => Fluent.Ribbon}/Themes/Office2013/Colors/ColorsWhite.xaml (97%) rename {Fluent => Fluent.Ribbon}/Themes/Office2013/Controls/ApplicationMenu.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2013/Controls/Backstage.xaml (97%) rename {Fluent => Fluent.Ribbon}/Themes/Office2013/Controls/BackstageControls.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2013/Controls/BackstageTabControl.xaml (97%) rename {Fluent => Fluent.Ribbon}/Themes/Office2013/Controls/BackstageTabItem.xaml (97%) rename {Fluent => Fluent.Ribbon}/Themes/Office2013/Controls/Button.xaml (97%) rename {Fluent => Fluent.Ribbon}/Themes/Office2013/Controls/CheckBox.xaml (97%) rename {Fluent => Fluent.Ribbon}/Themes/Office2013/Controls/ComboBox.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2013/Controls/ComboBoxItem.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2013/Controls/DropDownButton.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2013/Controls/Gallery.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2013/Controls/GalleryItem.xaml (97%) rename {Fluent => Fluent.Ribbon}/Themes/Office2013/Controls/InRibbonGallery.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2013/Controls/KeyTip.xaml (97%) rename {Fluent => Fluent.Ribbon}/Themes/Office2013/Controls/Menu.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2013/Controls/MenuItem.xaml (97%) rename {Fluent => Fluent.Ribbon}/Themes/Office2013/Controls/QuickAccessToolbar.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2013/Controls/Ribbon.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2013/Controls/RibbonContextualTabGroup.xaml (97%) rename {Fluent => Fluent.Ribbon}/Themes/Office2013/Controls/RibbonGroupBox.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2013/Controls/RibbonSeparator.xaml (97%) rename {Fluent => Fluent.Ribbon}/Themes/Office2013/Controls/RibbonStatusBar.xaml (97%) rename {Fluent => Fluent.Ribbon}/Themes/Office2013/Controls/RibbonTabControl.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2013/Controls/RibbonTabItem.xaml (97%) rename {Fluent => Fluent.Ribbon}/Themes/Office2013/Controls/ScreenTip.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2013/Controls/ScrollBar.xaml (97%) rename {Fluent => Fluent.Ribbon}/Themes/Office2013/Controls/Slider.xaml (97%) rename {Fluent => Fluent.Ribbon}/Themes/Office2013/Controls/Spinner.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2013/Controls/SplitButton.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2013/Controls/StatusBar.xaml (97%) rename {Fluent => Fluent.Ribbon}/Themes/Office2013/Controls/ToggleButton.xaml (97%) rename {Fluent => Fluent.Ribbon}/Themes/Office2013/Generic.txt (97%) create mode 100644 Fluent.Ribbon/Themes/Office2013/Generic.xaml rename {Fluent => Fluent.Ribbon}/Themes/Office2013/Images/Checked.png (100%) rename {Fluent => Fluent.Ribbon}/Themes/Office2013/Images/Copy.png (100%) rename {Fluent => Fluent.Ribbon}/Themes/Office2013/Images/Cut.png (100%) rename {Fluent => Fluent.Ribbon}/Themes/Office2013/Images/Help.png (100%) rename {Fluent => Fluent.Ribbon}/Themes/Office2013/Images/Images.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Office2013/Images/MoreColors.png (100%) rename {Fluent => Fluent.Ribbon}/Themes/Office2013/Images/Paste.png (100%) rename {Fluent => Fluent.Ribbon}/Themes/Office2013/Images/Warning.png (100%) rename {Fluent => Fluent.Ribbon}/Themes/Office2013/RibbonWindow.xaml (98%) rename {Fluent => Fluent.Ribbon}/Themes/Windows8/Colors/Colors.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Windows8/Colors/ColorsSilver.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Windows8/Controls/ApplicationMenu.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Windows8/Controls/ApplicationMenuItem.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Windows8/Controls/Backstage.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Windows8/Controls/BackstageControls.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Windows8/Controls/BackstageTabControl.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Windows8/Controls/BackstageTabItem.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Windows8/Controls/Button.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Windows8/Controls/CheckBox.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Windows8/Controls/ColorGallery.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Windows8/Controls/ComboBox.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Windows8/Controls/ComboBoxItem.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Windows8/Controls/DropDownButton.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Windows8/Controls/Gallery.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Windows8/Controls/GalleryItem.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Windows8/Controls/InRibbonGallery.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Windows8/Controls/Menu.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Windows8/Controls/MenuItem.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Windows8/Controls/QuickAccessToolbar.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Windows8/Controls/Ribbon.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Windows8/Controls/RibbonContextualTabGroup.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Windows8/Controls/RibbonGroupBox.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Windows8/Controls/RibbonSeparator.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Windows8/Controls/RibbonStatusBar.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Windows8/Controls/RibbonTabControl.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Windows8/Controls/RibbonTabItem.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Windows8/Controls/ScrollBar.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Windows8/Controls/Slider.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Windows8/Controls/Spinner.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Windows8/Controls/SplitButton.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Windows8/Controls/StatusBar.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Windows8/Controls/ToggleButton.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Windows8/Generic.txt (100%) create mode 100644 Fluent.Ribbon/Themes/Windows8/Generic.xaml rename {Fluent => Fluent.Ribbon}/Themes/Windows8/Images/Images.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Windows8/RibbonWindow.xaml (100%) rename {Fluent => Fluent.Ribbon}/Themes/Windows8/Silver.xaml (100%) rename {Fluent => Fluent.Ribbon}/packages.config (100%) delete mode 100644 Fluent/Themes/XamlCombine.exe diff --git a/Fluent.Ribbon.Showcase/Fluent.Ribbon.Showcase.NET 4.0.csproj b/Fluent.Ribbon.Showcase/Fluent.Ribbon.Showcase.NET 4.0.csproj index 399d3bd98..7dba610cc 100644 --- a/Fluent.Ribbon.Showcase/Fluent.Ribbon.Showcase.NET 4.0.csproj +++ b/Fluent.Ribbon.Showcase/Fluent.Ribbon.Showcase.NET 4.0.csproj @@ -150,9 +150,9 @@ - - {4C92FCF4-3561-499F-BC5B-F2F089863046} - Fluent dotNET 4.0 + + {281095D8-D8B3-4A7F-8896-646483FB685C} + Fluent.Ribbon.NET 4.0 diff --git a/Fluent.Ribbon.Showcase/Fluent.Ribbon.Showcase.NET 4.5.csproj b/Fluent.Ribbon.Showcase/Fluent.Ribbon.Showcase.NET 4.5.csproj index baff8624f..97964cdb5 100644 --- a/Fluent.Ribbon.Showcase/Fluent.Ribbon.Showcase.NET 4.5.csproj +++ b/Fluent.Ribbon.Showcase/Fluent.Ribbon.Showcase.NET 4.5.csproj @@ -157,9 +157,9 @@ - + {4C92FCF4-3561-499F-BC5B-F2F089863047} - Fluent dotNET 4.5 + Fluent.Ribbon.NET 4.5 diff --git a/Fluent.Ribbon.sln b/Fluent.Ribbon.sln index 602843f5d..5113d3349 100644 --- a/Fluent.Ribbon.sln +++ b/Fluent.Ribbon.sln @@ -5,7 +5,7 @@ VisualStudioVersion = 14.0.24720.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fluent.Ribbon.Showcase.NET 4.5", "Fluent.Ribbon.Showcase\Fluent.Ribbon.Showcase.NET 4.5.csproj", "{2C2A076B-E626-4A07-9D6E-4AEE3FEC41B6}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fluent dotNET 4.5", "Fluent\Fluent dotNET 4.5.csproj", "{4C92FCF4-3561-499F-BC5B-F2F089863047}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fluent.Ribbon.NET 4.5", "Fluent.Ribbon\Fluent.Ribbon.NET 4.5.csproj", "{4C92FCF4-3561-499F-BC5B-F2F089863047}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{B3EE3E07-9FD5-4282-9743-AC457EB45BBA}" ProjectSection(SolutionItems) = preProject @@ -15,7 +15,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{B3EE3E ReleaseNotes.md = ReleaseNotes.md EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fluent dotNET 4.0", "Fluent\Fluent dotNET 4.0.csproj", "{281095D8-D8B3-4A7F-8896-646483FB685C}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fluent.Ribbon.NET 4.0", "Fluent.Ribbon\Fluent.Ribbon.NET 4.0.csproj", "{281095D8-D8B3-4A7F-8896-646483FB685C}" ProjectSection(ProjectDependencies) = postProject {4C92FCF4-3561-499F-BC5B-F2F089863047} = {4C92FCF4-3561-499F-BC5B-F2F089863047} EndProjectSection diff --git a/Fluent/Adorners/KeyTipAdorner.cs b/Fluent.Ribbon/Adorners/KeyTipAdorner.cs similarity index 97% rename from Fluent/Adorners/KeyTipAdorner.cs rename to Fluent.Ribbon/Adorners/KeyTipAdorner.cs index 7f29026ae..c4c641c3f 100644 --- a/Fluent/Adorners/KeyTipAdorner.cs +++ b/Fluent.Ribbon/Adorners/KeyTipAdorner.cs @@ -1,1103 +1,1103 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Windows; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Interop; -using System.Windows.Media; -using System.Windows.Threading; - -namespace Fluent -{ - using System.Diagnostics; - - /// - /// Represents adorner for KeyTips. - /// KeyTipAdorners is chained to produce one from another. - /// Detaching root adorner couses detaching all adorners in the chain - /// - internal class KeyTipAdorner : Adorner - { - #region Events - - /// - /// This event is occured when adorner is - /// detached and is not able to be attached again - /// - public event EventHandler Terminated; - - #endregion - - #region Fields - - // KeyTips that have been - // found on this element - private readonly List keyTips = new List(); - private readonly List associatedElements = new List(); - private readonly FrameworkElement oneOfAssociatedElements; - private readonly Point[] keyTipPositions; - - // Parent adorner - private readonly KeyTipAdorner parentAdorner; - KeyTipAdorner childAdorner; - - // Focused element - private IInputElement focusedElement; - - private readonly Visibility[] backupedVisibilities; - private readonly UIElement keyTipElementContainer; - - // Is this adorner attached to the adorned element? - private bool attached; - private HwndSource attachedHwndSource; - - // Current entered chars - string enteredKeys = ""; - - // Designate that this adorner is terminated - private bool terminated; - - private DispatcherTimer timerFocusTracking; - - private AdornerLayer adornerLayer; - - #endregion - - #region Properties - - /// - /// Determines whether at least one on the adorners in the chain is alive - /// - public bool IsAdornerChainAlive - { - get { return this.attached || (this.childAdorner != null && this.childAdorner.IsAdornerChainAlive); } - } - - public bool AreAnyKeyTipsVisible - { - get { return this.keyTips.Any(x => x.IsVisible) || (this.childAdorner != null && this.childAdorner.AreAnyKeyTipsVisible); } - } - - public KeyTipAdorner ActiveKeyTipAdorner - { - get - { - return this.childAdorner != null && this.childAdorner.IsAdornerChainAlive - ? this.childAdorner.ActiveKeyTipAdorner - : this; - } - } - - #endregion - - #region Intialization - - /// - /// Construcotor - /// - /// - /// Parent adorner or null - /// The element which is container for elements - public KeyTipAdorner(UIElement adornedElement, UIElement keyTipElementContainer, KeyTipAdorner parentAdorner) - : base(adornedElement) - { - this.parentAdorner = parentAdorner; - - this.keyTipElementContainer = keyTipElementContainer; - - // Try to find supported elements - this.FindKeyTips(this.keyTipElementContainer, false); - this.oneOfAssociatedElements = (FrameworkElement)(this.associatedElements.Count != 0 - ? this.associatedElements[0] - : adornedElement // Maybe here is bug, coz we need keytipped item here... - ); - - this.keyTipPositions = new Point[this.keyTips.Count]; - this.backupedVisibilities = new Visibility[this.keyTips.Count]; - } - - // Find key tips on the given element - private void FindKeyTips(UIElement element, bool hide) - { - this.Log("FindKeyTips"); - - var children = LogicalTreeHelper.GetChildren(element); - foreach (var item in children) - { - var child = item as UIElement; - - if (child == null - || child.Visibility != Visibility.Visible) - { - continue; - } - - var groupBox = child as RibbonGroupBox; - - var keys = KeyTip.GetKeys(child); - if (keys != null) - { - // Gotcha! - var keyTip = new KeyTip - { - Content = keys, - Visibility = hide - ? Visibility.Collapsed - : Visibility.Visible - }; - - // Add to list & visual - // children collections - this.keyTips.Add(keyTip); - this.AddVisualChild(keyTip); - this.associatedElements.Add(child); - - if (groupBox != null) - { - if (groupBox.State == RibbonGroupBoxState.Collapsed) - { - keyTip.Visibility = Visibility.Visible; - this.FindKeyTips(child, true); - continue; - } - else - { - keyTip.Visibility = Visibility.Collapsed; - } - } - else - { - // Bind IsEnabled property - var binding = new Binding("IsEnabled") - { - Source = child, - Mode = BindingMode.OneWay - }; - keyTip.SetBinding(IsEnabledProperty, binding); - continue; - } - } - - if (groupBox != null && - groupBox.State == RibbonGroupBoxState.Collapsed) - { - this.FindKeyTips(child, true); - } - else - { - this.FindKeyTips(child, hide); - } - } - } - - #endregion - - #region Attach & Detach - - /// - /// Attaches this adorner to the adorned element - /// - public void Attach() - { - if (this.attached) - { - return; - } - - this.oneOfAssociatedElements.UpdateLayout(); - - this.Log("Attach begin {0}", this.Visibility); - - if (!this.oneOfAssociatedElements.IsLoaded) - { - // Delay attaching - this.Log("Delaying attach"); - this.oneOfAssociatedElements.Loaded += this.OnDelayAttach; - return; - } - - this.adornerLayer = GetAdornerLayer(this.oneOfAssociatedElements); - - if (this.adornerLayer == null) - { - this.Log("No adorner layer found"); - return; - } - - // Focus current adorned element - // Keyboard.Focus(adornedElement); - this.focusedElement = Keyboard.FocusedElement; - - if (this.focusedElement != null) - { - this.Log("Focus Attached to {0}", this.focusedElement); - this.focusedElement.LostKeyboardFocus += this.OnFocusLost; - this.focusedElement.PreviewKeyDown += this.OnPreviewKeyDown; - this.focusedElement.PreviewTextInput += this.OnFocusedElementPreviewTextInput; - } - else - { - this.Log("[!] Focus Setup Failed"); - } - - GetTopLevelElement(this.oneOfAssociatedElements).PreviewMouseDown += this.OnInputActionOccured; - - // Clears previous user input - this.enteredKeys = ""; - this.FilterKeyTips(); - - // Show this adorner - this.adornerLayer.Add(this); - - // Hookup window activation - this.attachedHwndSource = ((HwndSource)PresentationSource.FromVisual(this.oneOfAssociatedElements)); - if (this.attachedHwndSource != null) - { - this.attachedHwndSource.AddHook(this.WindowProc); - } - - // Start timer to track focus changing - if (this.timerFocusTracking == null) - { - this.timerFocusTracking = new DispatcherTimer(DispatcherPriority.ApplicationIdle, Dispatcher.CurrentDispatcher) - { - Interval = TimeSpan.FromMilliseconds(50) - }; - this.timerFocusTracking.Tick += this.OnTimerFocusTrackingTick; - } - - this.timerFocusTracking.Start(); - - this.attached = true; - - this.Log("Attach end"); - } - - private void OnTimerFocusTrackingTick(object sender, EventArgs e) - { - if (this.focusedElement == Keyboard.FocusedElement) - { - return; - } - - this.Log("Focus is changed, but focus lost is not occured"); - - if (this.focusedElement != null) - { - this.focusedElement.LostKeyboardFocus -= this.OnFocusLost; - this.focusedElement.PreviewKeyDown -= this.OnPreviewKeyDown; - this.focusedElement.PreviewTextInput -= this.OnFocusedElementPreviewTextInput; - } - - this.focusedElement = Keyboard.FocusedElement; - - if (this.focusedElement != null) - { - this.focusedElement.LostKeyboardFocus += this.OnFocusLost; - this.focusedElement.PreviewKeyDown += this.OnPreviewKeyDown; - this.focusedElement.PreviewTextInput += this.OnFocusedElementPreviewTextInput; - } - } - - // Window's messages hook up - private IntPtr WindowProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) - { - // Check whether window is deactivated (wParam == 0) - if ((msg == 6) && (wParam == IntPtr.Zero) && (this.attached)) - { - this.Log("The host window is deactivated, keytips will be terminated"); - this.Terminate(); - } - - return IntPtr.Zero; - } - - private void OnDelayAttach(object sender, EventArgs args) - { - this.Log("Delay attach (control loaded)"); - this.oneOfAssociatedElements.Loaded -= this.OnDelayAttach; - this.Attach(); - } - - /// - /// Detaches this adorner from the adorned element - /// - public void Detach() - { - if (this.childAdorner != null) - { - this.childAdorner.Detach(); - } - - if (!this.attached) - { - return; - } - - this.Log("Detach Begin"); - - // Remove window hookup - if ((this.attachedHwndSource != null) && (!this.attachedHwndSource.IsDisposed)) - { - // Crashes in a few time if invoke immediately ??? - this.AdornedElement.Dispatcher.BeginInvoke((System.Threading.ThreadStart)(() => this.attachedHwndSource.RemoveHook(this.WindowProc))); - } - - // Maybe adorner awaiting attaching, cancel it - this.oneOfAssociatedElements.Loaded -= this.OnDelayAttach; - - if (this.focusedElement != null) - { - this.focusedElement.LostKeyboardFocus -= this.OnFocusLost; - this.focusedElement.PreviewKeyDown -= this.OnPreviewKeyDown; - this.focusedElement.PreviewTextInput -= this.OnFocusedElementPreviewTextInput; - this.focusedElement = null; - } - - GetTopLevelElement(this.oneOfAssociatedElements).PreviewMouseDown -= this.OnInputActionOccured; - - // Show this adorner - this.adornerLayer.Remove(this); - // Clears previous user input - this.enteredKeys = ""; - this.attached = false; - - // Stop timer to track focus changing - this.timerFocusTracking.Stop(); - - this.Log("Detach End"); - } - - #endregion - - #region Termination - - /// - /// Terminate whole key tip's adorner chain - /// - public void Terminate() - { - if (this.terminated) - { - return; - } - - this.terminated = true; - - this.Detach(); - - if (this.parentAdorner != null) - { - this.parentAdorner.Terminate(); - } - - if (this.childAdorner != null) - { - this.childAdorner.Terminate(); - } - - if (this.Terminated != null) - { - this.Terminated(this, EventArgs.Empty); - } - - this.Log("Termination"); - } - - #endregion - - #region Event Handlers - - [SuppressMessage("Microsoft.Maintainability", "CA1502")] - private void OnPreviewKeyDown(object sender, KeyEventArgs e) - { - this.Log("Key Down {0} ({1})", e.Key, e.OriginalSource); - - if (e.IsRepeat - || this.Visibility == Visibility.Hidden) - { - return; - } - - if ((!(this.AdornedElement is ContextMenu)) && - ((e.Key == Key.Left) || (e.Key == Key.Right) || (e.Key == Key.Up) || (e.Key == Key.Down) || - (e.Key == Key.Enter) || (e.Key == Key.Tab))) - { - this.Visibility = Visibility.Hidden; - } - else if (e.Key == Key.Escape) - { - this.Back(); - e.Handled = true; - } - } - - private void OnFocusedElementPreviewTextInput(object sender, TextCompositionEventArgs e) - { - var keyToSearch = this.enteredKeys + e.Text; - - if (this.IsElementsStartWith(keyToSearch)) - { - this.enteredKeys += e.Text; - - var element = this.TryGetElement(this.enteredKeys); - - if (element != null) - { - this.Forward(element); - } - else - { - this.FilterKeyTips(); - } - - e.Handled = true; - } - else - { - System.Media.SystemSounds.Beep.Play(); - } - } - - private void OnInputActionOccured(object sender, RoutedEventArgs e) - { - if (!this.attached) - { - return; - } - - this.Log("Input Action, Keystips will be terminated"); - this.Terminate(); - } - - private void OnFocusLost(object sender, RoutedEventArgs e) - { - if (!this.attached) - { - return; - } - - this.Log("Focus Lost"); - - var previousFocusedElementElement = this.focusedElement; - this.focusedElement.LostKeyboardFocus -= this.OnFocusLost; - this.focusedElement.PreviewKeyDown -= this.OnPreviewKeyDown; - this.focusedElement.PreviewTextInput -= this.OnFocusedElementPreviewTextInput; - this.focusedElement = Keyboard.FocusedElement; - - if (this.focusedElement != null) - { - this.Log("Focus Changed from {0} to {1}", previousFocusedElementElement, this.focusedElement); - this.focusedElement.LostKeyboardFocus += this.OnFocusLost; - this.focusedElement.PreviewKeyDown += this.OnPreviewKeyDown; - this.focusedElement.PreviewTextInput += this.OnFocusedElementPreviewTextInput; - } - else - { - this.Log("Focus Not Restored"); - } - } - - #endregion - - #region Static Methods - - private static AdornerLayer GetAdornerLayer(UIElement element) - { - var current = element; - - while (true) - { - if (current == null) return null; - - var parent = (UIElement)VisualTreeHelper.GetParent(current) - ?? (UIElement)LogicalTreeHelper.GetParent(current); - - current = parent; - - if (current is AdornerDecorator) - { - return AdornerLayer.GetAdornerLayer((UIElement)VisualTreeHelper.GetChild(current, 0)); - } - } - } - - private static UIElement GetTopLevelElement(UIElement element) - { - var current = element; - - while (true) - { - current = (VisualTreeHelper.GetParent(element)) as UIElement; - - if (current == null) - { - return element; - } - - element = current; - } - } - - #endregion - - #region Methods - - // Back to the previous adorner - public void Back() - { - var control = this.keyTipElementContainer as IKeyTipedControl; - if (control != null) - { - control.OnKeyTipBack(); - } - - if (this.parentAdorner != null) - { - this.Log("Back"); - this.Detach(); - this.parentAdorner.Attach(); - } - else - { - this.Terminate(); - } - } - - /// - /// Forwards to the elements with the given keys - /// - /// Keys - /// If true the element will be clicked - /// If the element will be found the function will return true - public bool Forward(string keys, bool click) - { - this.Log("Trying to forward {0}", keys); - - var element = this.TryGetElement(keys); - if (element == null) - { - return false; - } - - this.Forward(element, click); - return true; - } - - // Forward to the next element - private void Forward(UIElement element) { - this.Forward(element, true); } - - // Forward to the next element - private void Forward(UIElement element, bool click) - { - this.Log("Forwarding"); - - this.Detach(); - - if (click) - { - //element.RaiseEvent(new RoutedEventArgs(Button.ClickEvent, null)); - var control = element as IKeyTipedControl; - if (control != null) - { - control.OnKeyTipPressed(); - } - } - - var children = LogicalTreeHelper.GetChildren(element) - .OfType() - .ToArray(); - - if (children.Length == 0) - { - this.Terminate(); - return; - } - - this.childAdorner = ReferenceEquals(GetTopLevelElement(children[0]), GetTopLevelElement(element)) == false - ? new KeyTipAdorner(children[0], element, this) - : new KeyTipAdorner(element, element, this); - - // Stop if no further KeyTips can be displayed. - if (!this.childAdorner.keyTips.Any()) - { - this.Terminate(); - return; - } - - this.childAdorner.Attach(); - } - - /// - /// Gets element keytipped by keys - /// - /// - /// Element - private UIElement TryGetElement(string keys) - { - for (var i = 0; i < this.keyTips.Count; i++) - { - if (!this.keyTips[i].IsEnabled - || - this.keyTips[i].Visibility != Visibility.Visible) - { - continue; - } - - var content = (string)this.keyTips[i].Content; - - if (keys.Equals(content, StringComparison.CurrentCultureIgnoreCase)) - { - return this.associatedElements[i]; - } - } - - return null; - } - - /// - /// Is one of the elements starts with the given chars - /// - /// - /// - private bool IsElementsStartWith(string keys) - { - foreach (var keyTip in this.keyTips.Where(x => x.IsEnabled)) - { - var content = (string)keyTip.Content; - - if (content.StartsWith(keys, StringComparison.CurrentCultureIgnoreCase)) - { - return true; - } - } - - return false; - } - - // Hide / unhide keytips relative matching to entered keys - private void FilterKeyTips() - { - this.Log("FilterKeyTips"); - - // Backup current visibility of key tips - for (var i = 0; i < this.backupedVisibilities.Length; i++) - { - this.backupedVisibilities[i] = this.keyTips[i].Visibility; - } - - // Hide / unhide keytips relative matching to entered keys - for (var i = 0; i < this.keyTips.Count; i++) - { - var content = (string)this.keyTips[i].Content; - - if (string.IsNullOrEmpty(this.enteredKeys)) - { - this.keyTips[i].Visibility = this.backupedVisibilities[i]; - } - else - { - this.keyTips[i].Visibility = content.StartsWith(this.enteredKeys, StringComparison.CurrentCultureIgnoreCase) - ? this.backupedVisibilities[i] - : Visibility.Collapsed; - } - } - - this.Log("Filtered key tips: {0}", this.keyTips.Count(x => x.Visibility == Visibility.Visible)); - } - - #endregion - - #region Layout & Visual Children - - /// - /// Positions child elements and determines - /// a size for the control - /// - /// The final area within the parent - /// that this element should use to arrange - /// itself and its children - /// The actual size used - protected override Size ArrangeOverride(Size finalSize) - { - this.Log("ArrangeOverride"); - - for (var i = 0; i < this.keyTips.Count; i++) - { - this.keyTips[i].Arrange(new Rect(this.keyTipPositions[i], this.keyTips[i].DesiredSize)); - } - - return finalSize; - } - - /// - /// Measures KeyTips - /// - /// The available size that this element can give to child elements. - /// The size that the groups container determines it needs during - /// layout, based on its calculations of child element sizes. - /// - protected override Size MeasureOverride(Size constraint) - { - this.Log("MeasureOverride"); - - var infinitySize = new Size(double.PositiveInfinity, double.PositiveInfinity); - foreach (var tip in this.keyTips) - { - tip.Measure(infinitySize); - } - - this.UpdateKeyTipPositions(); - - var result = new Size(0, 0); - for (var i = 0; i < this.keyTips.Count; i++) - { - var cornerX = this.keyTips[i].DesiredSize.Width + this.keyTipPositions[i].X; - var cornerY = this.keyTips[i].DesiredSize.Height + this.keyTipPositions[i].Y; - - if (cornerX > result.Width) - { - result.Width = cornerX; - } - - if (cornerY > result.Height) - { - result.Height = cornerY; - } - } - - return result; - } - - /// - /// Gets parent RibbonGroupBox or null - /// - /// - /// - private static RibbonGroupBox GetGroupBox(DependencyObject element) - { - if (element == null) - { - return null; - } - - var groupBox = element as RibbonGroupBox; - if (groupBox != null) - { - return groupBox; - } - - var parent = VisualTreeHelper.GetParent(element); - return GetGroupBox(parent); - } - - [SuppressMessage("Microsoft.Maintainability", "CA1502")] - private void UpdateKeyTipPositions() - { - this.Log("UpdateKeyTipPositions"); - - if (this.keyTips.Count == 0) - { - return; - } - - double[] rows = null; - var groupBox = GetGroupBox(this.oneOfAssociatedElements); - if (groupBox != null) - { - var panel = groupBox.GetPanel(); - if (panel != null) - { - var height = groupBox.GetLayoutRoot().DesiredSize.Height; - rows = new[] - { - groupBox.GetLayoutRoot().TranslatePoint(new Point(0, 0), this.AdornedElement).Y, - groupBox.GetLayoutRoot().TranslatePoint(new Point(0, panel.DesiredSize.Height / 2.0), this.AdornedElement).Y, - groupBox.GetLayoutRoot().TranslatePoint(new Point(0, panel.DesiredSize.Height), this.AdornedElement).Y, - groupBox.GetLayoutRoot().TranslatePoint(new Point(0, height + 1), this.AdornedElement).Y - }; - } - } - - for (var i = 0; i < this.keyTips.Count; i++) - { - // Skip invisible keytips - if (this.keyTips[i].Visibility != Visibility.Visible) - { - continue; - } - - // Update KeyTip Visibility - var associatedElementIsVisible = this.associatedElements[i].IsVisible; - var associatedElementInVisualTree = VisualTreeHelper.GetParent(this.associatedElements[i]) != null; - this.keyTips[i].Visibility = associatedElementIsVisible && associatedElementInVisualTree ? Visibility.Visible : Visibility.Collapsed; - - if (!KeyTip.GetAutoPlacement(this.associatedElements[i])) - { - #region Custom Placement - - var keyTipSize = this.keyTips[i].DesiredSize; - var elementSize = this.associatedElements[i].RenderSize; - - double x = 0, y = 0; - var margin = KeyTip.GetMargin(this.associatedElements[i]); - - switch (KeyTip.GetHorizontalAlignment(this.associatedElements[i])) - { - case HorizontalAlignment.Left: - x = margin.Left; - break; - case HorizontalAlignment.Right: - x = elementSize.Width - keyTipSize.Width - margin.Right; - break; - case HorizontalAlignment.Center: - case HorizontalAlignment.Stretch: - x = elementSize.Width / 2.0 - keyTipSize.Width / 2.0 + margin.Left; - break; - } - - switch (KeyTip.GetVerticalAlignment(this.associatedElements[i])) - { - case VerticalAlignment.Top: - y = margin.Top; - break; - case VerticalAlignment.Bottom: - y = elementSize.Height - keyTipSize.Height - margin.Bottom; - break; - case VerticalAlignment.Center: - case VerticalAlignment.Stretch: - y = elementSize.Height / 2.0 - keyTipSize.Height / 2.0 + margin.Top; - break; - } - - this.keyTipPositions[i] = this.associatedElements[i].TranslatePoint(new Point(x, y), this.AdornedElement); - - #endregion - } - else if (((FrameworkElement)this.associatedElements[i]).Name == "PART_DialogLauncherButton") - { - // Dialog Launcher Button Exclusive Placement - var keyTipSize = this.keyTips[i].DesiredSize; - var elementSize = this.associatedElements[i].RenderSize; - if (rows == null) - { - continue; - } - - this.keyTipPositions[i] = this.associatedElements[i].TranslatePoint(new Point( - elementSize.Width / 2.0 - keyTipSize.Width / 2.0, - 0), this.AdornedElement); - this.keyTipPositions[i].Y = rows[3]; - } - else if ((this.associatedElements[i] is InRibbonGallery && !((InRibbonGallery)this.associatedElements[i]).IsCollapsed)) - { - // InRibbonGallery Exclusive Placement - var keyTipSize = this.keyTips[i].DesiredSize; - var elementSize = this.associatedElements[i].RenderSize; - if (rows == null) - { - continue; - } - - this.keyTipPositions[i] = this.associatedElements[i].TranslatePoint(new Point( - elementSize.Width - keyTipSize.Width / 2.0, - 0), this.AdornedElement); - this.keyTipPositions[i].Y = rows[2] - keyTipSize.Height / 2; - } - else if ((this.associatedElements[i] is RibbonTabItem) || (this.associatedElements[i] is Backstage)) - { - // Ribbon Tab Item Exclusive Placement - var keyTipSize = this.keyTips[i].DesiredSize; - var elementSize = this.associatedElements[i].RenderSize; - this.keyTipPositions[i] = this.associatedElements[i].TranslatePoint(new Point( - elementSize.Width / 2.0 - keyTipSize.Width / 2.0, - elementSize.Height - keyTipSize.Height / 2.0), this.AdornedElement); - } - else if (this.associatedElements[i] is RibbonGroupBox) - { - // Ribbon Group Box Exclusive Placement - var keyTipSize = this.keyTips[i].DesiredSize; - var elementSize = this.associatedElements[i].DesiredSize; - this.keyTipPositions[i] = this.associatedElements[i].TranslatePoint(new Point( - elementSize.Width / 2.0 - keyTipSize.Width / 2.0, - elementSize.Height + 1), this.AdornedElement); - } - else if (IsWithinQuickAccessToolbar(this.associatedElements[i])) - { - var translatedPoint = this.associatedElements[i].TranslatePoint(new Point(this.associatedElements[i].DesiredSize.Width / 2.0 - this.keyTips[i].DesiredSize.Width / 2.0, this.associatedElements[i].DesiredSize.Height - this.keyTips[i].DesiredSize.Height / 2.0), this.AdornedElement); - this.keyTipPositions[i] = translatedPoint; - } - else if (this.associatedElements[i] is MenuItem) - { - // MenuItem Exclusive Placement - var keyTipSize = this.keyTips[i].DesiredSize; - var elementSize = this.associatedElements[i].DesiredSize; - this.keyTipPositions[i] = this.associatedElements[i].TranslatePoint( - new Point( - elementSize.Height / 2.0 + 2, - elementSize.Height / 2.0 + 2), this.AdornedElement); - } - else if (this.associatedElements[i] is BackstageTabItem) - { - // BackstageButton Exclusive Placement - var keyTipSize = this.keyTips[i].DesiredSize; - var elementSize = this.associatedElements[i].DesiredSize; - this.keyTipPositions[i] = this.associatedElements[i].TranslatePoint( - new Point( - 5, - elementSize.Height / 2.0 - keyTipSize.Height), this.AdornedElement); - } - else if (((FrameworkElement)this.associatedElements[i]).Parent is BackstageTabControl) - { - // Backstage Items Exclusive Placement - var keyTipSize = this.keyTips[i].DesiredSize; - var elementSize = this.associatedElements[i].DesiredSize; - this.keyTipPositions[i] = this.associatedElements[i].TranslatePoint( - new Point( - 20, - elementSize.Height / 2.0 - keyTipSize.Height), this.AdornedElement); - } - else - { - if ((RibbonProperties.GetSize(this.associatedElements[i]) != RibbonControlSize.Large) - || (this.associatedElements[i] is Spinner) - || (this.associatedElements[i] is ComboBox) - || (this.associatedElements[i] is TextBox) - || (this.associatedElements[i] is CheckBox)) - { - var withinRibbonToolbar = IsWithinRibbonToolbarInTwoLine(this.associatedElements[i]); - var translatedPoint = this.associatedElements[i].TranslatePoint(new Point(this.keyTips[i].DesiredSize.Width / 2.0, this.keyTips[i].DesiredSize.Height / 2.0), this.AdornedElement); - - // Snapping to rows if it present - if (rows != null) - { - var index = 0; - var mindistance = Math.Abs(rows[0] - translatedPoint.Y); - for (var j = 1; j < rows.Length; j++) - { - if (withinRibbonToolbar && j == 1) - { - continue; - } - - var distance = Math.Abs(rows[j] - translatedPoint.Y); - if (distance < mindistance) - { - mindistance = distance; - index = j; - } - } - - translatedPoint.Y = rows[index] - this.keyTips[i].DesiredSize.Height / 2.0; - } - - this.keyTipPositions[i] = translatedPoint; - } - else - { - var translatedPoint = this.associatedElements[i].TranslatePoint(new Point(this.associatedElements[i].DesiredSize.Width / 2.0 - this.keyTips[i].DesiredSize.Width / 2.0, this.associatedElements[i].DesiredSize.Height - 8), this.AdornedElement); - if (rows != null) - { - translatedPoint.Y = rows[2] - this.keyTips[i].DesiredSize.Height / 2.0; - } - - this.keyTipPositions[i] = translatedPoint; - } - } - } - } - - // Determines whether the element is children to RibbonToolBar - private static bool IsWithinRibbonToolbarInTwoLine(DependencyObject element) - { - var parent = LogicalTreeHelper.GetParent(element) as UIElement; - var ribbonToolBar = parent as RibbonToolBar; - if (ribbonToolBar != null) - { - var definition = ribbonToolBar.GetCurrentLayoutDefinition(); - if (definition == null) - { - return false; - } - - if (definition.RowCount == 2 || definition.Rows.Count == 2) - { - return true; - } - - return false; - } - - if (parent == null) - { - return false; - } - - return IsWithinRibbonToolbarInTwoLine(parent); - } - - // Determines whether the element is children to quick access toolbar - private static bool IsWithinQuickAccessToolbar(DependencyObject element) - { - var parent = LogicalTreeHelper.GetParent(element) as UIElement; - if (parent is QuickAccessToolBar) - { - return true; - } - - if (parent == null) - { - return false; - } - - return IsWithinQuickAccessToolbar(parent); - } - - /// - /// Gets visual children count - /// - protected override int VisualChildrenCount - { - get - { - return this.keyTips.Count; - } - } - - /// - /// Returns a child at the specified index from a collection of child elements - /// - /// The zero-based index of the requested child element in the collection - /// The requested child element - protected override Visual GetVisualChild(int index) - { - return this.keyTips[index]; - } - - #endregion - - #region Logging - - [SuppressMessage("Microsoft.Performance", "CA1822")] - [SuppressMessage("Microsoft.Performance", "CA1801")] - [Conditional("DEBUG")] - private void Log(string format, params object[] args) - { - var name = this.AdornedElement.GetType().Name; - - var headeredControl = this.AdornedElement as IHeaderedControl; - if (headeredControl != null) - { - name += string.Format(" ({0})", headeredControl.Header); - } - - Debug.WriteLine("[" + name + "] " + string.Format(format, args), "KeyTipAdorner"); - } - - #endregion - } +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Windows; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Interop; +using System.Windows.Media; +using System.Windows.Threading; + +namespace Fluent +{ + using System.Diagnostics; + + /// + /// Represents adorner for KeyTips. + /// KeyTipAdorners is chained to produce one from another. + /// Detaching root adorner couses detaching all adorners in the chain + /// + internal class KeyTipAdorner : Adorner + { + #region Events + + /// + /// This event is occured when adorner is + /// detached and is not able to be attached again + /// + public event EventHandler Terminated; + + #endregion + + #region Fields + + // KeyTips that have been + // found on this element + private readonly List keyTips = new List(); + private readonly List associatedElements = new List(); + private readonly FrameworkElement oneOfAssociatedElements; + private readonly Point[] keyTipPositions; + + // Parent adorner + private readonly KeyTipAdorner parentAdorner; + KeyTipAdorner childAdorner; + + // Focused element + private IInputElement focusedElement; + + private readonly Visibility[] backupedVisibilities; + private readonly UIElement keyTipElementContainer; + + // Is this adorner attached to the adorned element? + private bool attached; + private HwndSource attachedHwndSource; + + // Current entered chars + string enteredKeys = ""; + + // Designate that this adorner is terminated + private bool terminated; + + private DispatcherTimer timerFocusTracking; + + private AdornerLayer adornerLayer; + + #endregion + + #region Properties + + /// + /// Determines whether at least one on the adorners in the chain is alive + /// + public bool IsAdornerChainAlive + { + get { return this.attached || (this.childAdorner != null && this.childAdorner.IsAdornerChainAlive); } + } + + public bool AreAnyKeyTipsVisible + { + get { return this.keyTips.Any(x => x.IsVisible) || (this.childAdorner != null && this.childAdorner.AreAnyKeyTipsVisible); } + } + + public KeyTipAdorner ActiveKeyTipAdorner + { + get + { + return this.childAdorner != null && this.childAdorner.IsAdornerChainAlive + ? this.childAdorner.ActiveKeyTipAdorner + : this; + } + } + + #endregion + + #region Intialization + + /// + /// Construcotor + /// + /// + /// Parent adorner or null + /// The element which is container for elements + public KeyTipAdorner(UIElement adornedElement, UIElement keyTipElementContainer, KeyTipAdorner parentAdorner) + : base(adornedElement) + { + this.parentAdorner = parentAdorner; + + this.keyTipElementContainer = keyTipElementContainer; + + // Try to find supported elements + this.FindKeyTips(this.keyTipElementContainer, false); + this.oneOfAssociatedElements = (FrameworkElement)(this.associatedElements.Count != 0 + ? this.associatedElements[0] + : adornedElement // Maybe here is bug, coz we need keytipped item here... + ); + + this.keyTipPositions = new Point[this.keyTips.Count]; + this.backupedVisibilities = new Visibility[this.keyTips.Count]; + } + + // Find key tips on the given element + private void FindKeyTips(UIElement element, bool hide) + { + this.Log("FindKeyTips"); + + var children = LogicalTreeHelper.GetChildren(element); + foreach (var item in children) + { + var child = item as UIElement; + + if (child == null + || child.Visibility != Visibility.Visible) + { + continue; + } + + var groupBox = child as RibbonGroupBox; + + var keys = KeyTip.GetKeys(child); + if (keys != null) + { + // Gotcha! + var keyTip = new KeyTip + { + Content = keys, + Visibility = hide + ? Visibility.Collapsed + : Visibility.Visible + }; + + // Add to list & visual + // children collections + this.keyTips.Add(keyTip); + this.AddVisualChild(keyTip); + this.associatedElements.Add(child); + + if (groupBox != null) + { + if (groupBox.State == RibbonGroupBoxState.Collapsed) + { + keyTip.Visibility = Visibility.Visible; + this.FindKeyTips(child, true); + continue; + } + else + { + keyTip.Visibility = Visibility.Collapsed; + } + } + else + { + // Bind IsEnabled property + var binding = new Binding("IsEnabled") + { + Source = child, + Mode = BindingMode.OneWay + }; + keyTip.SetBinding(IsEnabledProperty, binding); + continue; + } + } + + if (groupBox != null && + groupBox.State == RibbonGroupBoxState.Collapsed) + { + this.FindKeyTips(child, true); + } + else + { + this.FindKeyTips(child, hide); + } + } + } + + #endregion + + #region Attach & Detach + + /// + /// Attaches this adorner to the adorned element + /// + public void Attach() + { + if (this.attached) + { + return; + } + + this.oneOfAssociatedElements.UpdateLayout(); + + this.Log("Attach begin {0}", this.Visibility); + + if (!this.oneOfAssociatedElements.IsLoaded) + { + // Delay attaching + this.Log("Delaying attach"); + this.oneOfAssociatedElements.Loaded += this.OnDelayAttach; + return; + } + + this.adornerLayer = GetAdornerLayer(this.oneOfAssociatedElements); + + if (this.adornerLayer == null) + { + this.Log("No adorner layer found"); + return; + } + + // Focus current adorned element + // Keyboard.Focus(adornedElement); + this.focusedElement = Keyboard.FocusedElement; + + if (this.focusedElement != null) + { + this.Log("Focus Attached to {0}", this.focusedElement); + this.focusedElement.LostKeyboardFocus += this.OnFocusLost; + this.focusedElement.PreviewKeyDown += this.OnPreviewKeyDown; + this.focusedElement.PreviewTextInput += this.OnFocusedElementPreviewTextInput; + } + else + { + this.Log("[!] Focus Setup Failed"); + } + + GetTopLevelElement(this.oneOfAssociatedElements).PreviewMouseDown += this.OnInputActionOccured; + + // Clears previous user input + this.enteredKeys = ""; + this.FilterKeyTips(); + + // Show this adorner + this.adornerLayer.Add(this); + + // Hookup window activation + this.attachedHwndSource = ((HwndSource)PresentationSource.FromVisual(this.oneOfAssociatedElements)); + if (this.attachedHwndSource != null) + { + this.attachedHwndSource.AddHook(this.WindowProc); + } + + // Start timer to track focus changing + if (this.timerFocusTracking == null) + { + this.timerFocusTracking = new DispatcherTimer(DispatcherPriority.ApplicationIdle, Dispatcher.CurrentDispatcher) + { + Interval = TimeSpan.FromMilliseconds(50) + }; + this.timerFocusTracking.Tick += this.OnTimerFocusTrackingTick; + } + + this.timerFocusTracking.Start(); + + this.attached = true; + + this.Log("Attach end"); + } + + private void OnTimerFocusTrackingTick(object sender, EventArgs e) + { + if (this.focusedElement == Keyboard.FocusedElement) + { + return; + } + + this.Log("Focus is changed, but focus lost is not occured"); + + if (this.focusedElement != null) + { + this.focusedElement.LostKeyboardFocus -= this.OnFocusLost; + this.focusedElement.PreviewKeyDown -= this.OnPreviewKeyDown; + this.focusedElement.PreviewTextInput -= this.OnFocusedElementPreviewTextInput; + } + + this.focusedElement = Keyboard.FocusedElement; + + if (this.focusedElement != null) + { + this.focusedElement.LostKeyboardFocus += this.OnFocusLost; + this.focusedElement.PreviewKeyDown += this.OnPreviewKeyDown; + this.focusedElement.PreviewTextInput += this.OnFocusedElementPreviewTextInput; + } + } + + // Window's messages hook up + private IntPtr WindowProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) + { + // Check whether window is deactivated (wParam == 0) + if ((msg == 6) && (wParam == IntPtr.Zero) && (this.attached)) + { + this.Log("The host window is deactivated, keytips will be terminated"); + this.Terminate(); + } + + return IntPtr.Zero; + } + + private void OnDelayAttach(object sender, EventArgs args) + { + this.Log("Delay attach (control loaded)"); + this.oneOfAssociatedElements.Loaded -= this.OnDelayAttach; + this.Attach(); + } + + /// + /// Detaches this adorner from the adorned element + /// + public void Detach() + { + if (this.childAdorner != null) + { + this.childAdorner.Detach(); + } + + if (!this.attached) + { + return; + } + + this.Log("Detach Begin"); + + // Remove window hookup + if ((this.attachedHwndSource != null) && (!this.attachedHwndSource.IsDisposed)) + { + // Crashes in a few time if invoke immediately ??? + this.AdornedElement.Dispatcher.BeginInvoke((System.Threading.ThreadStart)(() => this.attachedHwndSource.RemoveHook(this.WindowProc))); + } + + // Maybe adorner awaiting attaching, cancel it + this.oneOfAssociatedElements.Loaded -= this.OnDelayAttach; + + if (this.focusedElement != null) + { + this.focusedElement.LostKeyboardFocus -= this.OnFocusLost; + this.focusedElement.PreviewKeyDown -= this.OnPreviewKeyDown; + this.focusedElement.PreviewTextInput -= this.OnFocusedElementPreviewTextInput; + this.focusedElement = null; + } + + GetTopLevelElement(this.oneOfAssociatedElements).PreviewMouseDown -= this.OnInputActionOccured; + + // Show this adorner + this.adornerLayer.Remove(this); + // Clears previous user input + this.enteredKeys = ""; + this.attached = false; + + // Stop timer to track focus changing + this.timerFocusTracking.Stop(); + + this.Log("Detach End"); + } + + #endregion + + #region Termination + + /// + /// Terminate whole key tip's adorner chain + /// + public void Terminate() + { + if (this.terminated) + { + return; + } + + this.terminated = true; + + this.Detach(); + + if (this.parentAdorner != null) + { + this.parentAdorner.Terminate(); + } + + if (this.childAdorner != null) + { + this.childAdorner.Terminate(); + } + + if (this.Terminated != null) + { + this.Terminated(this, EventArgs.Empty); + } + + this.Log("Termination"); + } + + #endregion + + #region Event Handlers + + [SuppressMessage("Microsoft.Maintainability", "CA1502")] + private void OnPreviewKeyDown(object sender, KeyEventArgs e) + { + this.Log("Key Down {0} ({1})", e.Key, e.OriginalSource); + + if (e.IsRepeat + || this.Visibility == Visibility.Hidden) + { + return; + } + + if ((!(this.AdornedElement is ContextMenu)) && + ((e.Key == Key.Left) || (e.Key == Key.Right) || (e.Key == Key.Up) || (e.Key == Key.Down) || + (e.Key == Key.Enter) || (e.Key == Key.Tab))) + { + this.Visibility = Visibility.Hidden; + } + else if (e.Key == Key.Escape) + { + this.Back(); + e.Handled = true; + } + } + + private void OnFocusedElementPreviewTextInput(object sender, TextCompositionEventArgs e) + { + var keyToSearch = this.enteredKeys + e.Text; + + if (this.IsElementsStartWith(keyToSearch)) + { + this.enteredKeys += e.Text; + + var element = this.TryGetElement(this.enteredKeys); + + if (element != null) + { + this.Forward(element); + } + else + { + this.FilterKeyTips(); + } + + e.Handled = true; + } + else + { + System.Media.SystemSounds.Beep.Play(); + } + } + + private void OnInputActionOccured(object sender, RoutedEventArgs e) + { + if (!this.attached) + { + return; + } + + this.Log("Input Action, Keystips will be terminated"); + this.Terminate(); + } + + private void OnFocusLost(object sender, RoutedEventArgs e) + { + if (!this.attached) + { + return; + } + + this.Log("Focus Lost"); + + var previousFocusedElementElement = this.focusedElement; + this.focusedElement.LostKeyboardFocus -= this.OnFocusLost; + this.focusedElement.PreviewKeyDown -= this.OnPreviewKeyDown; + this.focusedElement.PreviewTextInput -= this.OnFocusedElementPreviewTextInput; + this.focusedElement = Keyboard.FocusedElement; + + if (this.focusedElement != null) + { + this.Log("Focus Changed from {0} to {1}", previousFocusedElementElement, this.focusedElement); + this.focusedElement.LostKeyboardFocus += this.OnFocusLost; + this.focusedElement.PreviewKeyDown += this.OnPreviewKeyDown; + this.focusedElement.PreviewTextInput += this.OnFocusedElementPreviewTextInput; + } + else + { + this.Log("Focus Not Restored"); + } + } + + #endregion + + #region Static Methods + + private static AdornerLayer GetAdornerLayer(UIElement element) + { + var current = element; + + while (true) + { + if (current == null) return null; + + var parent = (UIElement)VisualTreeHelper.GetParent(current) + ?? (UIElement)LogicalTreeHelper.GetParent(current); + + current = parent; + + if (current is AdornerDecorator) + { + return AdornerLayer.GetAdornerLayer((UIElement)VisualTreeHelper.GetChild(current, 0)); + } + } + } + + private static UIElement GetTopLevelElement(UIElement element) + { + var current = element; + + while (true) + { + current = (VisualTreeHelper.GetParent(element)) as UIElement; + + if (current == null) + { + return element; + } + + element = current; + } + } + + #endregion + + #region Methods + + // Back to the previous adorner + public void Back() + { + var control = this.keyTipElementContainer as IKeyTipedControl; + if (control != null) + { + control.OnKeyTipBack(); + } + + if (this.parentAdorner != null) + { + this.Log("Back"); + this.Detach(); + this.parentAdorner.Attach(); + } + else + { + this.Terminate(); + } + } + + /// + /// Forwards to the elements with the given keys + /// + /// Keys + /// If true the element will be clicked + /// If the element will be found the function will return true + public bool Forward(string keys, bool click) + { + this.Log("Trying to forward {0}", keys); + + var element = this.TryGetElement(keys); + if (element == null) + { + return false; + } + + this.Forward(element, click); + return true; + } + + // Forward to the next element + private void Forward(UIElement element) { + this.Forward(element, true); } + + // Forward to the next element + private void Forward(UIElement element, bool click) + { + this.Log("Forwarding"); + + this.Detach(); + + if (click) + { + //element.RaiseEvent(new RoutedEventArgs(Button.ClickEvent, null)); + var control = element as IKeyTipedControl; + if (control != null) + { + control.OnKeyTipPressed(); + } + } + + var children = LogicalTreeHelper.GetChildren(element) + .OfType() + .ToArray(); + + if (children.Length == 0) + { + this.Terminate(); + return; + } + + this.childAdorner = ReferenceEquals(GetTopLevelElement(children[0]), GetTopLevelElement(element)) == false + ? new KeyTipAdorner(children[0], element, this) + : new KeyTipAdorner(element, element, this); + + // Stop if no further KeyTips can be displayed. + if (!this.childAdorner.keyTips.Any()) + { + this.Terminate(); + return; + } + + this.childAdorner.Attach(); + } + + /// + /// Gets element keytipped by keys + /// + /// + /// Element + private UIElement TryGetElement(string keys) + { + for (var i = 0; i < this.keyTips.Count; i++) + { + if (!this.keyTips[i].IsEnabled + || + this.keyTips[i].Visibility != Visibility.Visible) + { + continue; + } + + var content = (string)this.keyTips[i].Content; + + if (keys.Equals(content, StringComparison.CurrentCultureIgnoreCase)) + { + return this.associatedElements[i]; + } + } + + return null; + } + + /// + /// Is one of the elements starts with the given chars + /// + /// + /// + private bool IsElementsStartWith(string keys) + { + foreach (var keyTip in this.keyTips.Where(x => x.IsEnabled)) + { + var content = (string)keyTip.Content; + + if (content.StartsWith(keys, StringComparison.CurrentCultureIgnoreCase)) + { + return true; + } + } + + return false; + } + + // Hide / unhide keytips relative matching to entered keys + private void FilterKeyTips() + { + this.Log("FilterKeyTips"); + + // Backup current visibility of key tips + for (var i = 0; i < this.backupedVisibilities.Length; i++) + { + this.backupedVisibilities[i] = this.keyTips[i].Visibility; + } + + // Hide / unhide keytips relative matching to entered keys + for (var i = 0; i < this.keyTips.Count; i++) + { + var content = (string)this.keyTips[i].Content; + + if (string.IsNullOrEmpty(this.enteredKeys)) + { + this.keyTips[i].Visibility = this.backupedVisibilities[i]; + } + else + { + this.keyTips[i].Visibility = content.StartsWith(this.enteredKeys, StringComparison.CurrentCultureIgnoreCase) + ? this.backupedVisibilities[i] + : Visibility.Collapsed; + } + } + + this.Log("Filtered key tips: {0}", this.keyTips.Count(x => x.Visibility == Visibility.Visible)); + } + + #endregion + + #region Layout & Visual Children + + /// + /// Positions child elements and determines + /// a size for the control + /// + /// The final area within the parent + /// that this element should use to arrange + /// itself and its children + /// The actual size used + protected override Size ArrangeOverride(Size finalSize) + { + this.Log("ArrangeOverride"); + + for (var i = 0; i < this.keyTips.Count; i++) + { + this.keyTips[i].Arrange(new Rect(this.keyTipPositions[i], this.keyTips[i].DesiredSize)); + } + + return finalSize; + } + + /// + /// Measures KeyTips + /// + /// The available size that this element can give to child elements. + /// The size that the groups container determines it needs during + /// layout, based on its calculations of child element sizes. + /// + protected override Size MeasureOverride(Size constraint) + { + this.Log("MeasureOverride"); + + var infinitySize = new Size(double.PositiveInfinity, double.PositiveInfinity); + foreach (var tip in this.keyTips) + { + tip.Measure(infinitySize); + } + + this.UpdateKeyTipPositions(); + + var result = new Size(0, 0); + for (var i = 0; i < this.keyTips.Count; i++) + { + var cornerX = this.keyTips[i].DesiredSize.Width + this.keyTipPositions[i].X; + var cornerY = this.keyTips[i].DesiredSize.Height + this.keyTipPositions[i].Y; + + if (cornerX > result.Width) + { + result.Width = cornerX; + } + + if (cornerY > result.Height) + { + result.Height = cornerY; + } + } + + return result; + } + + /// + /// Gets parent RibbonGroupBox or null + /// + /// + /// + private static RibbonGroupBox GetGroupBox(DependencyObject element) + { + if (element == null) + { + return null; + } + + var groupBox = element as RibbonGroupBox; + if (groupBox != null) + { + return groupBox; + } + + var parent = VisualTreeHelper.GetParent(element); + return GetGroupBox(parent); + } + + [SuppressMessage("Microsoft.Maintainability", "CA1502")] + private void UpdateKeyTipPositions() + { + this.Log("UpdateKeyTipPositions"); + + if (this.keyTips.Count == 0) + { + return; + } + + double[] rows = null; + var groupBox = GetGroupBox(this.oneOfAssociatedElements); + if (groupBox != null) + { + var panel = groupBox.GetPanel(); + if (panel != null) + { + var height = groupBox.GetLayoutRoot().DesiredSize.Height; + rows = new[] + { + groupBox.GetLayoutRoot().TranslatePoint(new Point(0, 0), this.AdornedElement).Y, + groupBox.GetLayoutRoot().TranslatePoint(new Point(0, panel.DesiredSize.Height / 2.0), this.AdornedElement).Y, + groupBox.GetLayoutRoot().TranslatePoint(new Point(0, panel.DesiredSize.Height), this.AdornedElement).Y, + groupBox.GetLayoutRoot().TranslatePoint(new Point(0, height + 1), this.AdornedElement).Y + }; + } + } + + for (var i = 0; i < this.keyTips.Count; i++) + { + // Skip invisible keytips + if (this.keyTips[i].Visibility != Visibility.Visible) + { + continue; + } + + // Update KeyTip Visibility + var associatedElementIsVisible = this.associatedElements[i].IsVisible; + var associatedElementInVisualTree = VisualTreeHelper.GetParent(this.associatedElements[i]) != null; + this.keyTips[i].Visibility = associatedElementIsVisible && associatedElementInVisualTree ? Visibility.Visible : Visibility.Collapsed; + + if (!KeyTip.GetAutoPlacement(this.associatedElements[i])) + { + #region Custom Placement + + var keyTipSize = this.keyTips[i].DesiredSize; + var elementSize = this.associatedElements[i].RenderSize; + + double x = 0, y = 0; + var margin = KeyTip.GetMargin(this.associatedElements[i]); + + switch (KeyTip.GetHorizontalAlignment(this.associatedElements[i])) + { + case HorizontalAlignment.Left: + x = margin.Left; + break; + case HorizontalAlignment.Right: + x = elementSize.Width - keyTipSize.Width - margin.Right; + break; + case HorizontalAlignment.Center: + case HorizontalAlignment.Stretch: + x = elementSize.Width / 2.0 - keyTipSize.Width / 2.0 + margin.Left; + break; + } + + switch (KeyTip.GetVerticalAlignment(this.associatedElements[i])) + { + case VerticalAlignment.Top: + y = margin.Top; + break; + case VerticalAlignment.Bottom: + y = elementSize.Height - keyTipSize.Height - margin.Bottom; + break; + case VerticalAlignment.Center: + case VerticalAlignment.Stretch: + y = elementSize.Height / 2.0 - keyTipSize.Height / 2.0 + margin.Top; + break; + } + + this.keyTipPositions[i] = this.associatedElements[i].TranslatePoint(new Point(x, y), this.AdornedElement); + + #endregion + } + else if (((FrameworkElement)this.associatedElements[i]).Name == "PART_DialogLauncherButton") + { + // Dialog Launcher Button Exclusive Placement + var keyTipSize = this.keyTips[i].DesiredSize; + var elementSize = this.associatedElements[i].RenderSize; + if (rows == null) + { + continue; + } + + this.keyTipPositions[i] = this.associatedElements[i].TranslatePoint(new Point( + elementSize.Width / 2.0 - keyTipSize.Width / 2.0, + 0), this.AdornedElement); + this.keyTipPositions[i].Y = rows[3]; + } + else if ((this.associatedElements[i] is InRibbonGallery && !((InRibbonGallery)this.associatedElements[i]).IsCollapsed)) + { + // InRibbonGallery Exclusive Placement + var keyTipSize = this.keyTips[i].DesiredSize; + var elementSize = this.associatedElements[i].RenderSize; + if (rows == null) + { + continue; + } + + this.keyTipPositions[i] = this.associatedElements[i].TranslatePoint(new Point( + elementSize.Width - keyTipSize.Width / 2.0, + 0), this.AdornedElement); + this.keyTipPositions[i].Y = rows[2] - keyTipSize.Height / 2; + } + else if ((this.associatedElements[i] is RibbonTabItem) || (this.associatedElements[i] is Backstage)) + { + // Ribbon Tab Item Exclusive Placement + var keyTipSize = this.keyTips[i].DesiredSize; + var elementSize = this.associatedElements[i].RenderSize; + this.keyTipPositions[i] = this.associatedElements[i].TranslatePoint(new Point( + elementSize.Width / 2.0 - keyTipSize.Width / 2.0, + elementSize.Height - keyTipSize.Height / 2.0), this.AdornedElement); + } + else if (this.associatedElements[i] is RibbonGroupBox) + { + // Ribbon Group Box Exclusive Placement + var keyTipSize = this.keyTips[i].DesiredSize; + var elementSize = this.associatedElements[i].DesiredSize; + this.keyTipPositions[i] = this.associatedElements[i].TranslatePoint(new Point( + elementSize.Width / 2.0 - keyTipSize.Width / 2.0, + elementSize.Height + 1), this.AdornedElement); + } + else if (IsWithinQuickAccessToolbar(this.associatedElements[i])) + { + var translatedPoint = this.associatedElements[i].TranslatePoint(new Point(this.associatedElements[i].DesiredSize.Width / 2.0 - this.keyTips[i].DesiredSize.Width / 2.0, this.associatedElements[i].DesiredSize.Height - this.keyTips[i].DesiredSize.Height / 2.0), this.AdornedElement); + this.keyTipPositions[i] = translatedPoint; + } + else if (this.associatedElements[i] is MenuItem) + { + // MenuItem Exclusive Placement + var keyTipSize = this.keyTips[i].DesiredSize; + var elementSize = this.associatedElements[i].DesiredSize; + this.keyTipPositions[i] = this.associatedElements[i].TranslatePoint( + new Point( + elementSize.Height / 2.0 + 2, + elementSize.Height / 2.0 + 2), this.AdornedElement); + } + else if (this.associatedElements[i] is BackstageTabItem) + { + // BackstageButton Exclusive Placement + var keyTipSize = this.keyTips[i].DesiredSize; + var elementSize = this.associatedElements[i].DesiredSize; + this.keyTipPositions[i] = this.associatedElements[i].TranslatePoint( + new Point( + 5, + elementSize.Height / 2.0 - keyTipSize.Height), this.AdornedElement); + } + else if (((FrameworkElement)this.associatedElements[i]).Parent is BackstageTabControl) + { + // Backstage Items Exclusive Placement + var keyTipSize = this.keyTips[i].DesiredSize; + var elementSize = this.associatedElements[i].DesiredSize; + this.keyTipPositions[i] = this.associatedElements[i].TranslatePoint( + new Point( + 20, + elementSize.Height / 2.0 - keyTipSize.Height), this.AdornedElement); + } + else + { + if ((RibbonProperties.GetSize(this.associatedElements[i]) != RibbonControlSize.Large) + || (this.associatedElements[i] is Spinner) + || (this.associatedElements[i] is ComboBox) + || (this.associatedElements[i] is TextBox) + || (this.associatedElements[i] is CheckBox)) + { + var withinRibbonToolbar = IsWithinRibbonToolbarInTwoLine(this.associatedElements[i]); + var translatedPoint = this.associatedElements[i].TranslatePoint(new Point(this.keyTips[i].DesiredSize.Width / 2.0, this.keyTips[i].DesiredSize.Height / 2.0), this.AdornedElement); + + // Snapping to rows if it present + if (rows != null) + { + var index = 0; + var mindistance = Math.Abs(rows[0] - translatedPoint.Y); + for (var j = 1; j < rows.Length; j++) + { + if (withinRibbonToolbar && j == 1) + { + continue; + } + + var distance = Math.Abs(rows[j] - translatedPoint.Y); + if (distance < mindistance) + { + mindistance = distance; + index = j; + } + } + + translatedPoint.Y = rows[index] - this.keyTips[i].DesiredSize.Height / 2.0; + } + + this.keyTipPositions[i] = translatedPoint; + } + else + { + var translatedPoint = this.associatedElements[i].TranslatePoint(new Point(this.associatedElements[i].DesiredSize.Width / 2.0 - this.keyTips[i].DesiredSize.Width / 2.0, this.associatedElements[i].DesiredSize.Height - 8), this.AdornedElement); + if (rows != null) + { + translatedPoint.Y = rows[2] - this.keyTips[i].DesiredSize.Height / 2.0; + } + + this.keyTipPositions[i] = translatedPoint; + } + } + } + } + + // Determines whether the element is children to RibbonToolBar + private static bool IsWithinRibbonToolbarInTwoLine(DependencyObject element) + { + var parent = LogicalTreeHelper.GetParent(element) as UIElement; + var ribbonToolBar = parent as RibbonToolBar; + if (ribbonToolBar != null) + { + var definition = ribbonToolBar.GetCurrentLayoutDefinition(); + if (definition == null) + { + return false; + } + + if (definition.RowCount == 2 || definition.Rows.Count == 2) + { + return true; + } + + return false; + } + + if (parent == null) + { + return false; + } + + return IsWithinRibbonToolbarInTwoLine(parent); + } + + // Determines whether the element is children to quick access toolbar + private static bool IsWithinQuickAccessToolbar(DependencyObject element) + { + var parent = LogicalTreeHelper.GetParent(element) as UIElement; + if (parent is QuickAccessToolBar) + { + return true; + } + + if (parent == null) + { + return false; + } + + return IsWithinQuickAccessToolbar(parent); + } + + /// + /// Gets visual children count + /// + protected override int VisualChildrenCount + { + get + { + return this.keyTips.Count; + } + } + + /// + /// Returns a child at the specified index from a collection of child elements + /// + /// The zero-based index of the requested child element in the collection + /// The requested child element + protected override Visual GetVisualChild(int index) + { + return this.keyTips[index]; + } + + #endregion + + #region Logging + + [SuppressMessage("Microsoft.Performance", "CA1822")] + [SuppressMessage("Microsoft.Performance", "CA1801")] + [Conditional("DEBUG")] + private void Log(string format, params object[] args) + { + var name = this.AdornedElement.GetType().Name; + + var headeredControl = this.AdornedElement as IHeaderedControl; + if (headeredControl != null) + { + name += string.Format(" ({0})", headeredControl.Header); + } + + Debug.WriteLine("[" + name + "] " + string.Format(format, args), "KeyTipAdorner"); + } + + #endregion + } } \ No newline at end of file diff --git a/Fluent/AttachedProperties/RibbonProperties.cs b/Fluent.Ribbon/AttachedProperties/RibbonProperties.cs similarity index 97% rename from Fluent/AttachedProperties/RibbonProperties.cs rename to Fluent.Ribbon/AttachedProperties/RibbonProperties.cs index 0c93ac927..381ae511d 100644 --- a/Fluent/AttachedProperties/RibbonProperties.cs +++ b/Fluent.Ribbon/AttachedProperties/RibbonProperties.cs @@ -1,169 +1,169 @@ -namespace Fluent -{ - using System.Diagnostics.CodeAnalysis; - using System.Windows; - using System.Windows.Media; - using Fluent.Extensibility; - - /// - /// Attached Properties for the Fluent Ribbon library - /// - public class RibbonProperties - { - #region TitleBarHeight Property - - /// - /// Using a DependencyProperty as the backing store for TitleBarHeight. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty TitleBarHeightProperty = - DependencyProperty.RegisterAttached("TitleBarHeight", typeof(double), typeof(RibbonProperties), - new FrameworkPropertyMetadata(25D, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.Inherits)); - - /// - /// Sets TitleBarHeight for element - /// - public static void SetTitleBarHeight(UIElement element, double value) - { - element.SetValue(TitleBarHeightProperty, value); - } - - /// - /// Gets TitleBarHeight for element - /// - [AttachedPropertyBrowsableForType(typeof(Ribbon))] - [AttachedPropertyBrowsableForType(typeof(RibbonTitleBar))] - [AttachedPropertyBrowsableForType(typeof(RibbonWindow))] - public static double GetTitleBarHeight(UIElement element) - { - return (double)element.GetValue(TitleBarHeightProperty); - } - - #endregion - - #region Size Property - - /// - /// Using a DependencyProperty as the backing store for Size. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty SizeProperty = - DependencyProperty.RegisterAttached("Size", typeof(RibbonControlSize), typeof(RibbonProperties), - new FrameworkPropertyMetadata(RibbonControlSize.Large, - FrameworkPropertyMetadataOptions.AffectsArrange | - FrameworkPropertyMetadataOptions.AffectsMeasure | - FrameworkPropertyMetadataOptions.AffectsRender | - FrameworkPropertyMetadataOptions.AffectsParentArrange | - FrameworkPropertyMetadataOptions.AffectsParentMeasure, - OnSizePropertyChanged) - ); - - private static void OnSizePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var sink = d as IRibbonSizeChangedSink; - - if (sink == null) - { - return; - } - - sink.OnSizePropertyChanged((RibbonControlSize)e.OldValue, (RibbonControlSize)e.NewValue); - } - - /// - /// Sets SizeDefinition for element - /// - public static void SetSize(DependencyObject element, RibbonControlSize value) - { - element.SetValue(SizeProperty, value); - } - - /// - /// Gets SizeDefinition for element - /// - public static RibbonControlSize GetSize(DependencyObject element) - { - return (RibbonControlSize)element.GetValue(SizeProperty); - } - - #endregion - - #region SizeDefinition Property - - /// - /// Using a DependencyProperty as the backing store for SizeDefinition. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty SizeDefinitionProperty = - DependencyProperty.RegisterAttached("SizeDefinition", typeof(RibbonControlSizeDefinition), typeof(RibbonProperties), - new FrameworkPropertyMetadata(new RibbonControlSizeDefinition(RibbonControlSize.Large, RibbonControlSize.Middle, RibbonControlSize.Small), - FrameworkPropertyMetadataOptions.AffectsArrange | - FrameworkPropertyMetadataOptions.AffectsMeasure | - FrameworkPropertyMetadataOptions.AffectsRender | - FrameworkPropertyMetadataOptions.AffectsParentArrange | - FrameworkPropertyMetadataOptions.AffectsParentMeasure, - OnSizeDefinitionPropertyChanged)); - - /// - /// Gets SizeDefinition for element - /// - public static RibbonControlSizeDefinition GetSizeDefinition(DependencyObject element) - { - return (RibbonControlSizeDefinition)element.GetValue(SizeDefinitionProperty); - } - - /// - /// Sets SizeDefinition for element - /// - public static void SetSizeDefinition(DependencyObject element, RibbonControlSizeDefinition value) - { - element.SetValue(SizeDefinitionProperty, value); - } - - // Handles RibbonSizeDefinitionProperty changes - internal static void OnSizeDefinitionPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - // Find parent group box - var groupBox = FindParentRibbonGroupBox(d); - var element = (UIElement)d; - - if (groupBox != null) - { - SetAppropriateSize(element, groupBox.State); - } - else - { - SetAppropriateSize(element, RibbonGroupBoxState.Large); - } - } - - // Finds parent group box - [SuppressMessage("Microsoft.Performance", "CA1800")] - internal static RibbonGroupBox FindParentRibbonGroupBox(DependencyObject o) - { - while (!(o is RibbonGroupBox)) - { - o = VisualTreeHelper.GetParent(o) ?? LogicalTreeHelper.GetParent(o); - if (o == null) - { - break; - } - } - - return (RibbonGroupBox)o; - } - - /// - /// Sets appropriate size of the control according to the - /// given group box state and control's size definition - /// - /// UI Element - /// Group box state - public static void SetAppropriateSize(DependencyObject element, RibbonGroupBoxState state) - { - SetSize(element, GetSizeDefinition(element).GetSize(state)); - } - - #endregion - } +namespace Fluent +{ + using System.Diagnostics.CodeAnalysis; + using System.Windows; + using System.Windows.Media; + using Fluent.Extensibility; + + /// + /// Attached Properties for the Fluent Ribbon library + /// + public class RibbonProperties + { + #region TitleBarHeight Property + + /// + /// Using a DependencyProperty as the backing store for TitleBarHeight. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty TitleBarHeightProperty = + DependencyProperty.RegisterAttached("TitleBarHeight", typeof(double), typeof(RibbonProperties), + new FrameworkPropertyMetadata(25D, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.Inherits)); + + /// + /// Sets TitleBarHeight for element + /// + public static void SetTitleBarHeight(UIElement element, double value) + { + element.SetValue(TitleBarHeightProperty, value); + } + + /// + /// Gets TitleBarHeight for element + /// + [AttachedPropertyBrowsableForType(typeof(Ribbon))] + [AttachedPropertyBrowsableForType(typeof(RibbonTitleBar))] + [AttachedPropertyBrowsableForType(typeof(RibbonWindow))] + public static double GetTitleBarHeight(UIElement element) + { + return (double)element.GetValue(TitleBarHeightProperty); + } + + #endregion + + #region Size Property + + /// + /// Using a DependencyProperty as the backing store for Size. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty SizeProperty = + DependencyProperty.RegisterAttached("Size", typeof(RibbonControlSize), typeof(RibbonProperties), + new FrameworkPropertyMetadata(RibbonControlSize.Large, + FrameworkPropertyMetadataOptions.AffectsArrange | + FrameworkPropertyMetadataOptions.AffectsMeasure | + FrameworkPropertyMetadataOptions.AffectsRender | + FrameworkPropertyMetadataOptions.AffectsParentArrange | + FrameworkPropertyMetadataOptions.AffectsParentMeasure, + OnSizePropertyChanged) + ); + + private static void OnSizePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var sink = d as IRibbonSizeChangedSink; + + if (sink == null) + { + return; + } + + sink.OnSizePropertyChanged((RibbonControlSize)e.OldValue, (RibbonControlSize)e.NewValue); + } + + /// + /// Sets SizeDefinition for element + /// + public static void SetSize(DependencyObject element, RibbonControlSize value) + { + element.SetValue(SizeProperty, value); + } + + /// + /// Gets SizeDefinition for element + /// + public static RibbonControlSize GetSize(DependencyObject element) + { + return (RibbonControlSize)element.GetValue(SizeProperty); + } + + #endregion + + #region SizeDefinition Property + + /// + /// Using a DependencyProperty as the backing store for SizeDefinition. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty SizeDefinitionProperty = + DependencyProperty.RegisterAttached("SizeDefinition", typeof(RibbonControlSizeDefinition), typeof(RibbonProperties), + new FrameworkPropertyMetadata(new RibbonControlSizeDefinition(RibbonControlSize.Large, RibbonControlSize.Middle, RibbonControlSize.Small), + FrameworkPropertyMetadataOptions.AffectsArrange | + FrameworkPropertyMetadataOptions.AffectsMeasure | + FrameworkPropertyMetadataOptions.AffectsRender | + FrameworkPropertyMetadataOptions.AffectsParentArrange | + FrameworkPropertyMetadataOptions.AffectsParentMeasure, + OnSizeDefinitionPropertyChanged)); + + /// + /// Gets SizeDefinition for element + /// + public static RibbonControlSizeDefinition GetSizeDefinition(DependencyObject element) + { + return (RibbonControlSizeDefinition)element.GetValue(SizeDefinitionProperty); + } + + /// + /// Sets SizeDefinition for element + /// + public static void SetSizeDefinition(DependencyObject element, RibbonControlSizeDefinition value) + { + element.SetValue(SizeDefinitionProperty, value); + } + + // Handles RibbonSizeDefinitionProperty changes + internal static void OnSizeDefinitionPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + // Find parent group box + var groupBox = FindParentRibbonGroupBox(d); + var element = (UIElement)d; + + if (groupBox != null) + { + SetAppropriateSize(element, groupBox.State); + } + else + { + SetAppropriateSize(element, RibbonGroupBoxState.Large); + } + } + + // Finds parent group box + [SuppressMessage("Microsoft.Performance", "CA1800")] + internal static RibbonGroupBox FindParentRibbonGroupBox(DependencyObject o) + { + while (!(o is RibbonGroupBox)) + { + o = VisualTreeHelper.GetParent(o) ?? LogicalTreeHelper.GetParent(o); + if (o == null) + { + break; + } + } + + return (RibbonGroupBox)o; + } + + /// + /// Sets appropriate size of the control according to the + /// given group box state and control's size definition + /// + /// UI Element + /// Group box state + public static void SetAppropriateSize(DependencyObject element, RibbonGroupBoxState state) + { + SetSize(element, GetSizeDefinition(element).GetSize(state)); + } + + #endregion + } } \ No newline at end of file diff --git a/Fluent/BackstageButton.cs b/Fluent.Ribbon/BackstageButton.cs similarity index 97% rename from Fluent/BackstageButton.cs rename to Fluent.Ribbon/BackstageButton.cs index aae7aa73f..203273c6e 100644 --- a/Fluent/BackstageButton.cs +++ b/Fluent.Ribbon/BackstageButton.cs @@ -1,148 +1,148 @@ -#region Copyright and License Information -// Fluent Ribbon Control Suite -// http://fluent.codeplex.com/ -// Copyright Degtyarev Daniel, Rikker Serg. 2009-2010. All rights reserved. -// -// Distributed under the terms of the Microsoft Public License (Ms-PL). -// The license is available online http://fluent.codeplex.com/license -#endregion - -using System; -using System.Collections; -using System.Diagnostics.CodeAnalysis; -using System.Windows; -using System.Windows.Data; - -namespace Fluent -{ - /// - /// Represents backstage button - /// - public class BackstageButton:RibbonControl - { - #region Properties - - /// - /// Gets or sets Backstage - /// - public Backstage Backstage - { - get { return (Backstage)GetValue(BackstageProperty); } - set { SetValue(BackstageProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Backstage. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty BackstageProperty = - DependencyProperty.Register("Backstage", typeof(object), - typeof(BackstageButton), new UIPropertyMetadata(null)); - - /// - /// Gets or sets whether backstage is shown - /// - public bool IsOpen - { - get { return (bool)GetValue(IsOpenProperty); } - set { SetValue(IsOpenProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for IsOpen. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IsOpenProperty = - DependencyProperty.Register("IsOpen", typeof(bool), - typeof(BackstageButton), new UIPropertyMetadata(false)); - - /// - /// Gets an enumerator for logical child elements of this element. - /// - protected override IEnumerator LogicalChildren - { - get - { - ArrayList list = new ArrayList(); - list.Add(Backstage); - return list.GetEnumerator(); - } - } - - #endregion - - #region Initialization - - /// - /// Static constructor - /// - [SuppressMessage("Microsoft.Performance", "CA1810")] - static BackstageButton() - { - DefaultStyleKeyProperty.OverrideMetadata(typeof(BackstageButton), new FrameworkPropertyMetadata(typeof(BackstageButton))); - // Disable QAT for this control - CanAddToQuickAccessToolBarProperty.OverrideMetadata(typeof(BackstageButton), new FrameworkPropertyMetadata(false)); - } - - /// - /// Default constructor - /// - public BackstageButton() - { - Backstage = new Backstage(); - Binding binding = new Binding("Background"); - binding.Source = this; - Backstage.SetBinding(Backstage.BackgroundProperty, binding); - AddLogicalChild(Backstage); - } - - /// - /// Handles click event - /// - void Click() - { - IsOpen = !IsOpen; - } - - #endregion - - #region Overrides - - /// - /// Invoked when an unhandled System.Windows.UIElement.PreviewMouseLeftButtonDownrouted event reaches an element - /// in its route that is derived from this class. Implement this method to add class handling for this event. - /// - /// The System.Windows.Input.MouseButtonEventArgs that contains the event data. - /// The event data reports that the left mouse button was pressed. - protected override void OnMouseLeftButtonDown(System.Windows.Input.MouseButtonEventArgs e) - { - Click(); - } - - /// - /// Handles key tip pressed - /// - public override void OnKeyTipPressed() - { - Click(); - base.OnKeyTipPressed(); - } - - #endregion - - #region Quick Access Toolbar - - /// - /// Gets control which represents shortcut item. - /// This item MUST be syncronized with the original - /// and send command to original one control. - /// - /// Control which represents shortcut item - public override FrameworkElement CreateQuickAccessItem() - { - throw new NotImplementedException(); - } - - #endregion - } -} +#region Copyright and License Information +// Fluent Ribbon Control Suite +// http://fluent.codeplex.com/ +// Copyright Degtyarev Daniel, Rikker Serg. 2009-2010. All rights reserved. +// +// Distributed under the terms of the Microsoft Public License (Ms-PL). +// The license is available online http://fluent.codeplex.com/license +#endregion + +using System; +using System.Collections; +using System.Diagnostics.CodeAnalysis; +using System.Windows; +using System.Windows.Data; + +namespace Fluent +{ + /// + /// Represents backstage button + /// + public class BackstageButton:RibbonControl + { + #region Properties + + /// + /// Gets or sets Backstage + /// + public Backstage Backstage + { + get { return (Backstage)GetValue(BackstageProperty); } + set { SetValue(BackstageProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Backstage. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty BackstageProperty = + DependencyProperty.Register("Backstage", typeof(object), + typeof(BackstageButton), new UIPropertyMetadata(null)); + + /// + /// Gets or sets whether backstage is shown + /// + public bool IsOpen + { + get { return (bool)GetValue(IsOpenProperty); } + set { SetValue(IsOpenProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for IsOpen. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IsOpenProperty = + DependencyProperty.Register("IsOpen", typeof(bool), + typeof(BackstageButton), new UIPropertyMetadata(false)); + + /// + /// Gets an enumerator for logical child elements of this element. + /// + protected override IEnumerator LogicalChildren + { + get + { + ArrayList list = new ArrayList(); + list.Add(Backstage); + return list.GetEnumerator(); + } + } + + #endregion + + #region Initialization + + /// + /// Static constructor + /// + [SuppressMessage("Microsoft.Performance", "CA1810")] + static BackstageButton() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(BackstageButton), new FrameworkPropertyMetadata(typeof(BackstageButton))); + // Disable QAT for this control + CanAddToQuickAccessToolBarProperty.OverrideMetadata(typeof(BackstageButton), new FrameworkPropertyMetadata(false)); + } + + /// + /// Default constructor + /// + public BackstageButton() + { + Backstage = new Backstage(); + Binding binding = new Binding("Background"); + binding.Source = this; + Backstage.SetBinding(Backstage.BackgroundProperty, binding); + AddLogicalChild(Backstage); + } + + /// + /// Handles click event + /// + void Click() + { + IsOpen = !IsOpen; + } + + #endregion + + #region Overrides + + /// + /// Invoked when an unhandled System.Windows.UIElement.PreviewMouseLeftButtonDownrouted event reaches an element + /// in its route that is derived from this class. Implement this method to add class handling for this event. + /// + /// The System.Windows.Input.MouseButtonEventArgs that contains the event data. + /// The event data reports that the left mouse button was pressed. + protected override void OnMouseLeftButtonDown(System.Windows.Input.MouseButtonEventArgs e) + { + Click(); + } + + /// + /// Handles key tip pressed + /// + public override void OnKeyTipPressed() + { + Click(); + base.OnKeyTipPressed(); + } + + #endregion + + #region Quick Access Toolbar + + /// + /// Gets control which represents shortcut item. + /// This item MUST be syncronized with the original + /// and send command to original one control. + /// + /// Control which represents shortcut item + public override FrameworkElement CreateQuickAccessItem() + { + throw new NotImplementedException(); + } + + #endregion + } +} diff --git a/Fluent/Controls/ApplicationMenu.cs b/Fluent.Ribbon/Controls/ApplicationMenu.cs similarity index 97% rename from Fluent/Controls/ApplicationMenu.cs rename to Fluent.Ribbon/Controls/ApplicationMenu.cs index 294db6473..a36bd6f4b 100644 --- a/Fluent/Controls/ApplicationMenu.cs +++ b/Fluent.Ribbon/Controls/ApplicationMenu.cs @@ -1,121 +1,121 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.Windows; - -namespace Fluent -{ - /// - /// Represents backstage button - /// - public class ApplicationMenu : DropDownButton - { - #region Properties - - /// - /// Gets or sets width of right content - /// - public double RightPaneWidth - { - get { return (double)this.GetValue(RightPaneWidthProperty); } - set { this.SetValue(RightPaneWidthProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for RightContentWidth. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty RightPaneWidthProperty = - DependencyProperty.Register("RightPaneWidth", typeof(double), typeof(ApplicationMenu), new UIPropertyMetadata(300.0)); - - /// - /// Gets or sets application menu right pane content - /// - public object RightPaneContent - { - get { return this.GetValue(RightPaneContentProperty); } - set { this.SetValue(RightPaneContentProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for RightContent. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty RightPaneContentProperty = - DependencyProperty.Register("RightPaneContent", typeof(object), typeof(ApplicationMenu), new UIPropertyMetadata(null)); - - /// - /// Gets or sets application menu bottom pane content - /// - public object FooterPaneContent - { - get { return this.GetValue(FooterPaneContentProperty); } - set { this.SetValue(FooterPaneContentProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for BottomContent. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty FooterPaneContentProperty = - DependencyProperty.Register("FooterPaneContent", typeof(object), typeof(ApplicationMenu), new UIPropertyMetadata(null)); - - #endregion - - #region Initialization - - /// - /// Static constructor - /// - [SuppressMessage("Microsoft.Performance", "CA1810")] - static ApplicationMenu() - { - var type = typeof (ApplicationMenu); - - // Override style metadata - DefaultStyleKeyProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(type)); - // Disable QAT for this control - CanAddToQuickAccessToolBarProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(false)); - // Make default KeyTip - KeyTipProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(null, null, CoerceKeyTipKeys)); - StyleProperty.OverrideMetadata(typeof(ApplicationMenu), new FrameworkPropertyMetadata(null, OnCoerceStyle)); - } - - // Coerce object style - static object OnCoerceStyle(DependencyObject d, object basevalue) - { - if (basevalue == null) - { - basevalue = ((FrameworkElement) d).TryFindResource(typeof(ApplicationMenu)); - } - - return basevalue; - } - - static object CoerceKeyTipKeys(DependencyObject d, object basevalue) - { - return basevalue ?? Ribbon.Localization.BackstageButtonKeyTip; - } - - /// - /// Default constructor - /// - public ApplicationMenu() - { - this.CoerceValue(KeyTipProperty); - } - - #endregion - - #region Quick Access Toolbar - - /// - /// Gets control which represents shortcut item. - /// This item MUST be syncronized with the original - /// and send command to original one control. - /// - /// Control which represents shortcut item - public override FrameworkElement CreateQuickAccessItem() - { - throw new NotImplementedException(); - } - - #endregion - } +using System; +using System.Diagnostics.CodeAnalysis; +using System.Windows; + +namespace Fluent +{ + /// + /// Represents backstage button + /// + public class ApplicationMenu : DropDownButton + { + #region Properties + + /// + /// Gets or sets width of right content + /// + public double RightPaneWidth + { + get { return (double)this.GetValue(RightPaneWidthProperty); } + set { this.SetValue(RightPaneWidthProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for RightContentWidth. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty RightPaneWidthProperty = + DependencyProperty.Register("RightPaneWidth", typeof(double), typeof(ApplicationMenu), new UIPropertyMetadata(300.0)); + + /// + /// Gets or sets application menu right pane content + /// + public object RightPaneContent + { + get { return this.GetValue(RightPaneContentProperty); } + set { this.SetValue(RightPaneContentProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for RightContent. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty RightPaneContentProperty = + DependencyProperty.Register("RightPaneContent", typeof(object), typeof(ApplicationMenu), new UIPropertyMetadata(null)); + + /// + /// Gets or sets application menu bottom pane content + /// + public object FooterPaneContent + { + get { return this.GetValue(FooterPaneContentProperty); } + set { this.SetValue(FooterPaneContentProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for BottomContent. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty FooterPaneContentProperty = + DependencyProperty.Register("FooterPaneContent", typeof(object), typeof(ApplicationMenu), new UIPropertyMetadata(null)); + + #endregion + + #region Initialization + + /// + /// Static constructor + /// + [SuppressMessage("Microsoft.Performance", "CA1810")] + static ApplicationMenu() + { + var type = typeof (ApplicationMenu); + + // Override style metadata + DefaultStyleKeyProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(type)); + // Disable QAT for this control + CanAddToQuickAccessToolBarProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(false)); + // Make default KeyTip + KeyTipProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(null, null, CoerceKeyTipKeys)); + StyleProperty.OverrideMetadata(typeof(ApplicationMenu), new FrameworkPropertyMetadata(null, OnCoerceStyle)); + } + + // Coerce object style + static object OnCoerceStyle(DependencyObject d, object basevalue) + { + if (basevalue == null) + { + basevalue = ((FrameworkElement) d).TryFindResource(typeof(ApplicationMenu)); + } + + return basevalue; + } + + static object CoerceKeyTipKeys(DependencyObject d, object basevalue) + { + return basevalue ?? Ribbon.Localization.BackstageButtonKeyTip; + } + + /// + /// Default constructor + /// + public ApplicationMenu() + { + this.CoerceValue(KeyTipProperty); + } + + #endregion + + #region Quick Access Toolbar + + /// + /// Gets control which represents shortcut item. + /// This item MUST be syncronized with the original + /// and send command to original one control. + /// + /// Control which represents shortcut item + public override FrameworkElement CreateQuickAccessItem() + { + throw new NotImplementedException(); + } + + #endregion + } } \ No newline at end of file diff --git a/Fluent/Controls/Backstage.cs b/Fluent.Ribbon/Controls/Backstage.cs similarity index 96% rename from Fluent/Controls/Backstage.cs rename to Fluent.Ribbon/Controls/Backstage.cs index 00e026054..bf33eea59 100644 --- a/Fluent/Controls/Backstage.cs +++ b/Fluent.Ribbon/Controls/Backstage.cs @@ -1,707 +1,707 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.ComponentModel; -using System.Diagnostics.CodeAnalysis; -using System.Windows; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Interop; -using System.Windows.Markup; -using System.Windows.Media; - -namespace Fluent -{ - using System.Threading; - using System.Threading.Tasks; - using System.Windows.Controls; - using System.Windows.Threading; - using Fluent.Extensions; - using Fluent.Internal; - - /// - /// Represents backstage button - /// - [ContentProperty("Content")] - public class Backstage : RibbonControl - { - private static readonly object syncIsOpen = new object(); - - #region Events - - /// - /// Occurs when IsOpen has been changed - /// - public event DependencyPropertyChangedEventHandler IsOpenChanged; - - #endregion - - #region Fields - - // Adorner for backstage - BackstageAdorner adorner; - - #endregion - - #region Properties - - #region IsOpen - - /// - /// Gets or sets whether backstage is shown - /// - public bool IsOpen - { - get { return (bool)this.GetValue(IsOpenProperty); } - set { this.SetValue(IsOpenProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for IsOpen. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IsOpenProperty = - DependencyProperty.Register("IsOpen", typeof(bool), - typeof(Backstage), new UIPropertyMetadata(false, OnIsOpenChanged)); - - /// - /// Gets or sets the duration for the hide animation - /// - public Duration HideAnimationDuration - { - get { return (Duration)this.GetValue(HideAnimationDurationProperty); } - set { this.SetValue(HideAnimationDurationProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for HideAnimationDuration. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty HideAnimationDurationProperty = DependencyProperty.Register("HideAnimationDuration", typeof(Duration), typeof(Backstage), new PropertyMetadata(null)); - - /// - /// Gets or sets whether context tabs on the titlebar should be hidden when backstage is open - /// - public bool HideContextTabsOnOpen - { - get { return (bool)this.GetValue(HideContextTabsOnOpenProperty); } - set { this.SetValue(HideContextTabsOnOpenProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for HideContextTabsOnOpen. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty HideContextTabsOnOpenProperty = DependencyProperty.Register("HideContextTabsOnOpen", typeof(bool), typeof(Backstage), new PropertyMetadata(false)); - - /// - /// Gets or sets whether to close the backstage when Esc is pressed - /// - public bool CloseOnEsc - { - get { return (bool)this.GetValue(CloseOnEscProperty); } - set { this.SetValue(CloseOnEscProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for CloseOnEsc. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty CloseOnEscProperty = DependencyProperty.Register("CloseOnEsc", typeof(bool), typeof(Backstage), new PropertyMetadata(true)); - - private static void OnIsOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var backstage = (Backstage)d; - - lock (syncIsOpen) - { - if ((bool)e.NewValue) - { - backstage.Show(); - } - else - { - if (backstage.HideAnimationDuration.HasTimeSpan) - { - var timespan = backstage.HideAnimationDuration.TimeSpan; - - Task.Factory.StartNew(() => - { - Thread.Sleep(timespan); - - backstage.Dispatcher.RunInDispatcher(backstage.Hide); - }); - } - else - { - backstage.Hide(); - } - } - - // Invoke the event - if (backstage.IsOpenChanged != null) - { - backstage.IsOpenChanged(backstage, e); - } - } - } - - #endregion - - #region Content - - /// - /// Gets or sets content of the backstage - /// - public UIElement Content - { - get { return (UIElement)this.GetValue(ContentProperty); } - set { this.SetValue(ContentProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Content. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty ContentProperty = - DependencyProperty.Register("Content", typeof(UIElement), typeof(Backstage), - new UIPropertyMetadata(null, OnContentChanged)); - - static void OnContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var backstage = (Backstage)d; - if (e.OldValue != null) - { - backstage.RemoveLogicalChild(e.OldValue); - } - - if (e.NewValue != null) - { - backstage.AddLogicalChild(e.NewValue); - } - } - - #endregion - - #region LogicalChildren - - /// - /// Gets an enumerator for logical child elements of this element. - /// - protected override IEnumerator LogicalChildren - { - get - { - if (this.Content != null) - { - yield return this.Content; - } - } - } - - #endregion - - #endregion - - #region Initialization - - /// - /// Static constructor - /// - [SuppressMessage("Microsoft.Performance", "CA1810")] - static Backstage() - { - DefaultStyleKeyProperty.OverrideMetadata(typeof(Backstage), new FrameworkPropertyMetadata(typeof(Backstage))); - // Disable QAT for this control - CanAddToQuickAccessToolBarProperty.OverrideMetadata(typeof(Backstage), new FrameworkPropertyMetadata(false)); - - KeyboardNavigation.TabNavigationProperty.OverrideMetadata(typeof(Backstage), new FrameworkPropertyMetadata(KeyboardNavigationMode.Cycle)); - } - - /// - /// Default constructor - /// - public Backstage() - { - this.Loaded += this.OnBackstageLoaded; - this.Unloaded += this.OnBackstageUnloaded; - } - - private void OnPopupDismiss(object sender, DismissPopupEventArgs e) - { - this.IsOpen = false; - } - - #endregion - - #region Methods - - // Handles click event - private void Click() - { - this.IsOpen = !this.IsOpen; - } - - #region Show / Hide - - // We have to collapse WindowsFormsHost while Backstate is open - private readonly Dictionary collapsedElements = new Dictionary(); - - // Saved window sizes - private double savedWindowMinWidth = double.NaN; - private double savedWindowMinHeight = double.NaN; - private double savedWindowWidth = double.NaN; - private double savedWindowHeight = double.NaN; - - // Opens backstage on an Adorner layer - private void Show() - { - // don't open the backstage while in design mode - if (DesignerProperties.GetIsInDesignMode(this)) - { - return; - } - - if (this.IsLoaded == false) - { - this.Loaded += this.OnDelayedShow; - return; - } - - if (this.Content == null) - { - return; - } - - this.CreateAndAttachBackstageAdorner(); - - this.ShowAdorner(); - - var ribbon = this.FindRibbon(); - if (ribbon != null) - { - ribbon.TabControl.IsDropDownOpen = false; - ribbon.TabControl.HighlightSelectedItem = false; - ribbon.TabControl.RequestBackstageClose += this.OnTabControlRequestBackstageClose; - - // Disable QAT & title bar - if (ribbon.QuickAccessToolBar != null) - { - ribbon.QuickAccessToolBar.IsEnabled = false; - } - - if (ribbon.TitleBar != null) - { - ribbon.TitleBar.IsEnabled = false; - ribbon.TitleBar.HideContextTabs = this.HideContextTabsOnOpen; - } - } - - var window = Window.GetWindow(this); - - this.SaveWindowSize(window); - this.SaveWindowMinSize(window); - - if (window != null) - { - window.KeyDown += this.HandleWindowKeyDown; - - - if (this.savedWindowMinWidth < 500) - { - window.MinWidth = 500; - } - - if (this.savedWindowMinHeight < 400) - { - window.MinHeight = 400; - } - - window.SizeChanged += this.OnWindowSizeChanged; - - // We have to collapse WindowsFormsHost while Backstage is open - this.CollapseWindowsFormsHosts(window); - } - - var content = this.Content as IInputElement; - if (content != null) - { - content.Focus(); - } - } - - private void ShowAdorner() - { - if (this.adorner == null) - { - return; - } - - this.adorner.Visibility = Visibility.Visible; - } - - private void HideAdorner() - { - if (this.adorner == null) - { - return; - } - - this.adorner.Visibility = Visibility.Collapsed; - } - - private void CreateAndAttachBackstageAdorner() - { - if (this.adorner != null) - { - return; - } - - FrameworkElement topLevelElement = null; - - if (DesignerProperties.GetIsInDesignMode(this)) - { - // TODO: in design mode it is required to use design time adorner - topLevelElement = (FrameworkElement)VisualTreeHelper.GetParent(this); - } - else - { - var mainWindow = Window.GetWindow(this); - if (mainWindow == null) - { - return; - } - - var content = mainWindow.Content; - - var fe = content as FrameworkElement; // Content may be an arbitrary .NET object when set using a databinding and using data templates. - - if (fe != null) - { - topLevelElement = fe; - } - else - { - // If Content is not a FrameworkElement we try to find the ContentPresenter - // containing the template to display the content. - var contentPresenter = UIHelper.FindVisualChild(mainWindow); - - if (contentPresenter != null && contentPresenter.Content == content) - { - // set the root element of the template as the top level element. - topLevelElement = (FrameworkElement)VisualTreeHelper.GetChild(contentPresenter, 0); - } - } - } - - if (topLevelElement == null) - { - return; - } - - this.adorner = new BackstageAdorner(topLevelElement, this); - - var layer = AdornerLayer.GetAdornerLayer(this); - layer.Add(this.adorner); - - layer.CommandBindings.Add(new CommandBinding(RibbonCommands.OpenBackstage, - (sender, args) => - { - this.IsOpen = !this.IsOpen; - })); - } - - - private void DestroyAdorner() - { - if (this.adorner == null) - { - return; - } - - var layer = AdornerLayer.GetAdornerLayer(this); - layer.Remove(this.adorner); - - this.adorner.Clear(); - this.adorner = null; - } - - private void OnDelayedShow(object sender, EventArgs args) - { - this.Loaded -= this.OnDelayedShow; - - // Delaying show so everthing can load properly. - // If we don't run this in the background setting IsOpen=true on application start we don't have access to the Bastage from the BackstageTabControl. - this.RunInDispatcherAsync(this.Show, DispatcherPriority.Background); - } - - // Hide backstage - private void Hide() - { - this.Loaded -= this.OnDelayedShow; - - if (this.Content == null) - { - return; - } - - if (!this.IsLoaded - || this.adorner == null) - { - return; - } - - this.HideAdorner(); - - var ribbon = this.FindRibbon(); - if (ribbon != null) - { - ribbon.TabControl.HighlightSelectedItem = true; - ribbon.TabControl.RequestBackstageClose -= this.OnTabControlRequestBackstageClose; - - // Restore enable under QAT & title bar - if (ribbon.QuickAccessToolBar != null) - { - ribbon.QuickAccessToolBar.IsEnabled = true; - ribbon.QuickAccessToolBar.Refresh(); - } - - if (ribbon.TitleBar != null) - { - ribbon.TitleBar.IsEnabled = true; - ribbon.TitleBar.HideContextTabs = false; - } - } - - var window = Window.GetWindow(this); - if (window != null) - { - window.PreviewKeyDown -= this.HandleWindowKeyDown; - window.SizeChanged -= this.OnWindowSizeChanged; - - if (double.IsNaN(this.savedWindowMinWidth) == false - && double.IsNaN(this.savedWindowMinHeight) == false) - { - window.MinWidth = this.savedWindowMinWidth; - window.MinHeight = this.savedWindowMinHeight; - } - - if (double.IsNaN(this.savedWindowWidth) == false - && double.IsNaN(this.savedWindowHeight) == false) - { - window.Width = this.savedWindowWidth; - window.Height = this.savedWindowHeight; - } - } - - // Uncollapse elements - foreach (var element in this.collapsedElements) - { - element.Key.Visibility = element.Value; - } - - this.collapsedElements.Clear(); - } - - // Finds underlying ribbon control - private Ribbon FindRibbon() - { - DependencyObject item = this; - - while (item != null - && !(item is Ribbon)) - { - item = VisualTreeHelper.GetParent(item); - } - - return (Ribbon)item; - } - - private void SaveWindowMinSize(Window window) - { - if (window == null) - { - this.savedWindowMinWidth = double.NaN; - this.savedWindowMinHeight = double.NaN; - return; - } - - this.savedWindowMinWidth = window.MinWidth; - this.savedWindowMinHeight = window.MinHeight; - } - - private void SaveWindowSize(Window window) - { - if (window == null) - { - this.savedWindowWidth = double.NaN; - this.savedWindowHeight = double.NaN; - return; - } - - this.savedWindowWidth = window.ActualWidth; - this.savedWindowHeight = window.ActualHeight; - } - - private void OnWindowSizeChanged(object sender, SizeChangedEventArgs e) - { - this.SaveWindowSize(Window.GetWindow(this)); - } - - private void OnTabControlRequestBackstageClose(object sender, EventArgs e) - { - this.IsOpen = false; - } - - // We have to collapse WindowsFormsHost while Backstage is open - private void CollapseWindowsFormsHosts(DependencyObject parent) - { - if (parent == null) - { - return; - } - - var frameworkElement = parent as FrameworkElement; - - // Do not hide contents in the backstage area - if (parent is BackstageAdorner) return; - - if (frameworkElement != null) - { - if ((parent is HwndHost) && - frameworkElement.Visibility != Visibility.Collapsed) - { - this.collapsedElements.Add(frameworkElement, frameworkElement.Visibility); - frameworkElement.Visibility = Visibility.Collapsed; - return; - } - } - - // Traverse visual tree - for (var i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++) - { - this.CollapseWindowsFormsHosts(VisualTreeHelper.GetChild(parent, i)); - } - } - - /// - /// Invoked when an unhandled attached event reaches an element in its route that is derived from this class. Implement this method to add class handling for this event. - /// - /// The that contains the event data. - protected override void OnKeyDown(KeyEventArgs e) - { - if (e.Handled) - { - return; - } - - switch (e.Key) - { - case Key.Enter: - case Key.Space: - if (this.IsFocused) - { - this.IsOpen = !this.IsOpen; - e.Handled = true; - } - break; - } - - base.OnKeyDown(e); - } - - // Handles backstage Esc key keydown - private void HandleWindowKeyDown(object sender, KeyEventArgs e) - { - if (this.CloseOnEsc && e.Key == Key.Escape) - { - // only handle ESC when the backstage is open - e.Handled = this.IsOpen; - - this.IsOpen = false; - } - } - - private void OnBackstageLoaded(object sender, RoutedEventArgs e) - { - this.AddHandler(PopupService.DismissPopupEvent, (DismissPopupEventHandler)this.OnPopupDismiss); - } - - private void OnBackstageUnloaded(object sender, RoutedEventArgs e) - { - this.RemoveHandler(PopupService.DismissPopupEvent, (DismissPopupEventHandler)this.OnPopupDismiss); - } - - #endregion - - #endregion - - #region Overrides - - /// - /// Invoked when an unhandled System.Windows.UIElement.PreviewMouseLeftButtonDownrouted event reaches an element - /// in its route that is derived from this class. Implement this method to add class handling for this event. - /// - /// The System.Windows.Input.MouseButtonEventArgs that contains the event data. - /// The event data reports that the left mouse button was pressed. - protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) - { - this.Click(); - } - - /// - /// Handles key tip pressed - /// - public override void OnKeyTipPressed() - { - this.IsOpen = true; - base.OnKeyTipPressed(); - } - - /// - /// Handles back navigation with KeyTips - /// - public override void OnKeyTipBack() - { - this.IsOpen = false; - base.OnKeyTipBack(); - } - - /// - /// When overridden in a derived class, is invoked whenever application code or internal processes call . - /// - public override void OnApplyTemplate() - { - base.OnApplyTemplate(); - - if (this.IsOpen) - { - this.Hide(); - } - - this.DestroyAdorner(); - - if (this.IsOpen) - { - this.Show(); - } - } - - #endregion - - #region Quick Access Toolbar - - /// - /// Gets control which represents shortcut item. - /// This item MUST be syncronized with the original - /// and send command to original one control. - /// - /// Control which represents shortcut item - public override FrameworkElement CreateQuickAccessItem() - { - throw new NotImplementedException(); - } - - #endregion - } +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Windows; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Interop; +using System.Windows.Markup; +using System.Windows.Media; + +namespace Fluent +{ + using System.Threading; + using System.Threading.Tasks; + using System.Windows.Controls; + using System.Windows.Threading; + using Fluent.Extensions; + using Fluent.Internal; + + /// + /// Represents backstage button + /// + [ContentProperty("Content")] + public class Backstage : RibbonControl + { + private static readonly object syncIsOpen = new object(); + + #region Events + + /// + /// Occurs when IsOpen has been changed + /// + public event DependencyPropertyChangedEventHandler IsOpenChanged; + + #endregion + + #region Fields + + // Adorner for backstage + BackstageAdorner adorner; + + #endregion + + #region Properties + + #region IsOpen + + /// + /// Gets or sets whether backstage is shown + /// + public bool IsOpen + { + get { return (bool)this.GetValue(IsOpenProperty); } + set { this.SetValue(IsOpenProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for IsOpen. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IsOpenProperty = + DependencyProperty.Register("IsOpen", typeof(bool), + typeof(Backstage), new UIPropertyMetadata(false, OnIsOpenChanged)); + + /// + /// Gets or sets the duration for the hide animation + /// + public Duration HideAnimationDuration + { + get { return (Duration)this.GetValue(HideAnimationDurationProperty); } + set { this.SetValue(HideAnimationDurationProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for HideAnimationDuration. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty HideAnimationDurationProperty = DependencyProperty.Register("HideAnimationDuration", typeof(Duration), typeof(Backstage), new PropertyMetadata(null)); + + /// + /// Gets or sets whether context tabs on the titlebar should be hidden when backstage is open + /// + public bool HideContextTabsOnOpen + { + get { return (bool)this.GetValue(HideContextTabsOnOpenProperty); } + set { this.SetValue(HideContextTabsOnOpenProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for HideContextTabsOnOpen. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty HideContextTabsOnOpenProperty = DependencyProperty.Register("HideContextTabsOnOpen", typeof(bool), typeof(Backstage), new PropertyMetadata(false)); + + /// + /// Gets or sets whether to close the backstage when Esc is pressed + /// + public bool CloseOnEsc + { + get { return (bool)this.GetValue(CloseOnEscProperty); } + set { this.SetValue(CloseOnEscProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for CloseOnEsc. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty CloseOnEscProperty = DependencyProperty.Register("CloseOnEsc", typeof(bool), typeof(Backstage), new PropertyMetadata(true)); + + private static void OnIsOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var backstage = (Backstage)d; + + lock (syncIsOpen) + { + if ((bool)e.NewValue) + { + backstage.Show(); + } + else + { + if (backstage.HideAnimationDuration.HasTimeSpan) + { + var timespan = backstage.HideAnimationDuration.TimeSpan; + + Task.Factory.StartNew(() => + { + Thread.Sleep(timespan); + + backstage.Dispatcher.RunInDispatcher(backstage.Hide); + }); + } + else + { + backstage.Hide(); + } + } + + // Invoke the event + if (backstage.IsOpenChanged != null) + { + backstage.IsOpenChanged(backstage, e); + } + } + } + + #endregion + + #region Content + + /// + /// Gets or sets content of the backstage + /// + public UIElement Content + { + get { return (UIElement)this.GetValue(ContentProperty); } + set { this.SetValue(ContentProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Content. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty ContentProperty = + DependencyProperty.Register("Content", typeof(UIElement), typeof(Backstage), + new UIPropertyMetadata(null, OnContentChanged)); + + static void OnContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var backstage = (Backstage)d; + if (e.OldValue != null) + { + backstage.RemoveLogicalChild(e.OldValue); + } + + if (e.NewValue != null) + { + backstage.AddLogicalChild(e.NewValue); + } + } + + #endregion + + #region LogicalChildren + + /// + /// Gets an enumerator for logical child elements of this element. + /// + protected override IEnumerator LogicalChildren + { + get + { + if (this.Content != null) + { + yield return this.Content; + } + } + } + + #endregion + + #endregion + + #region Initialization + + /// + /// Static constructor + /// + [SuppressMessage("Microsoft.Performance", "CA1810")] + static Backstage() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(Backstage), new FrameworkPropertyMetadata(typeof(Backstage))); + // Disable QAT for this control + CanAddToQuickAccessToolBarProperty.OverrideMetadata(typeof(Backstage), new FrameworkPropertyMetadata(false)); + + KeyboardNavigation.TabNavigationProperty.OverrideMetadata(typeof(Backstage), new FrameworkPropertyMetadata(KeyboardNavigationMode.Cycle)); + } + + /// + /// Default constructor + /// + public Backstage() + { + this.Loaded += this.OnBackstageLoaded; + this.Unloaded += this.OnBackstageUnloaded; + } + + private void OnPopupDismiss(object sender, DismissPopupEventArgs e) + { + this.IsOpen = false; + } + + #endregion + + #region Methods + + // Handles click event + private void Click() + { + this.IsOpen = !this.IsOpen; + } + + #region Show / Hide + + // We have to collapse WindowsFormsHost while Backstate is open + private readonly Dictionary collapsedElements = new Dictionary(); + + // Saved window sizes + private double savedWindowMinWidth = double.NaN; + private double savedWindowMinHeight = double.NaN; + private double savedWindowWidth = double.NaN; + private double savedWindowHeight = double.NaN; + + // Opens backstage on an Adorner layer + private void Show() + { + // don't open the backstage while in design mode + if (DesignerProperties.GetIsInDesignMode(this)) + { + return; + } + + if (this.IsLoaded == false) + { + this.Loaded += this.OnDelayedShow; + return; + } + + if (this.Content == null) + { + return; + } + + this.CreateAndAttachBackstageAdorner(); + + this.ShowAdorner(); + + var ribbon = this.FindRibbon(); + if (ribbon != null) + { + ribbon.TabControl.IsDropDownOpen = false; + ribbon.TabControl.HighlightSelectedItem = false; + ribbon.TabControl.RequestBackstageClose += this.OnTabControlRequestBackstageClose; + + // Disable QAT & title bar + if (ribbon.QuickAccessToolBar != null) + { + ribbon.QuickAccessToolBar.IsEnabled = false; + } + + if (ribbon.TitleBar != null) + { + ribbon.TitleBar.IsEnabled = false; + ribbon.TitleBar.HideContextTabs = this.HideContextTabsOnOpen; + } + } + + var window = Window.GetWindow(this); + + this.SaveWindowSize(window); + this.SaveWindowMinSize(window); + + if (window != null) + { + window.KeyDown += this.HandleWindowKeyDown; + + + if (this.savedWindowMinWidth < 500) + { + window.MinWidth = 500; + } + + if (this.savedWindowMinHeight < 400) + { + window.MinHeight = 400; + } + + window.SizeChanged += this.OnWindowSizeChanged; + + // We have to collapse WindowsFormsHost while Backstage is open + this.CollapseWindowsFormsHosts(window); + } + + var content = this.Content as IInputElement; + if (content != null) + { + content.Focus(); + } + } + + private void ShowAdorner() + { + if (this.adorner == null) + { + return; + } + + this.adorner.Visibility = Visibility.Visible; + } + + private void HideAdorner() + { + if (this.adorner == null) + { + return; + } + + this.adorner.Visibility = Visibility.Collapsed; + } + + private void CreateAndAttachBackstageAdorner() + { + if (this.adorner != null) + { + return; + } + + FrameworkElement topLevelElement = null; + + if (DesignerProperties.GetIsInDesignMode(this)) + { + // TODO: in design mode it is required to use design time adorner + topLevelElement = (FrameworkElement)VisualTreeHelper.GetParent(this); + } + else + { + var mainWindow = Window.GetWindow(this); + if (mainWindow == null) + { + return; + } + + var content = mainWindow.Content; + + var fe = content as FrameworkElement; // Content may be an arbitrary .NET object when set using a databinding and using data templates. + + if (fe != null) + { + topLevelElement = fe; + } + else + { + // If Content is not a FrameworkElement we try to find the ContentPresenter + // containing the template to display the content. + var contentPresenter = UIHelper.FindVisualChild(mainWindow); + + if (contentPresenter != null && contentPresenter.Content == content) + { + // set the root element of the template as the top level element. + topLevelElement = (FrameworkElement)VisualTreeHelper.GetChild(contentPresenter, 0); + } + } + } + + if (topLevelElement == null) + { + return; + } + + this.adorner = new BackstageAdorner(topLevelElement, this); + + var layer = AdornerLayer.GetAdornerLayer(this); + layer.Add(this.adorner); + + layer.CommandBindings.Add(new CommandBinding(RibbonCommands.OpenBackstage, + (sender, args) => + { + this.IsOpen = !this.IsOpen; + })); + } + + + private void DestroyAdorner() + { + if (this.adorner == null) + { + return; + } + + var layer = AdornerLayer.GetAdornerLayer(this); + layer.Remove(this.adorner); + + this.adorner.Clear(); + this.adorner = null; + } + + private void OnDelayedShow(object sender, EventArgs args) + { + this.Loaded -= this.OnDelayedShow; + + // Delaying show so everthing can load properly. + // If we don't run this in the background setting IsOpen=true on application start we don't have access to the Bastage from the BackstageTabControl. + this.RunInDispatcherAsync(this.Show, DispatcherPriority.Background); + } + + // Hide backstage + private void Hide() + { + this.Loaded -= this.OnDelayedShow; + + if (this.Content == null) + { + return; + } + + if (!this.IsLoaded + || this.adorner == null) + { + return; + } + + this.HideAdorner(); + + var ribbon = this.FindRibbon(); + if (ribbon != null) + { + ribbon.TabControl.HighlightSelectedItem = true; + ribbon.TabControl.RequestBackstageClose -= this.OnTabControlRequestBackstageClose; + + // Restore enable under QAT & title bar + if (ribbon.QuickAccessToolBar != null) + { + ribbon.QuickAccessToolBar.IsEnabled = true; + ribbon.QuickAccessToolBar.Refresh(); + } + + if (ribbon.TitleBar != null) + { + ribbon.TitleBar.IsEnabled = true; + ribbon.TitleBar.HideContextTabs = false; + } + } + + var window = Window.GetWindow(this); + if (window != null) + { + window.PreviewKeyDown -= this.HandleWindowKeyDown; + window.SizeChanged -= this.OnWindowSizeChanged; + + if (double.IsNaN(this.savedWindowMinWidth) == false + && double.IsNaN(this.savedWindowMinHeight) == false) + { + window.MinWidth = this.savedWindowMinWidth; + window.MinHeight = this.savedWindowMinHeight; + } + + if (double.IsNaN(this.savedWindowWidth) == false + && double.IsNaN(this.savedWindowHeight) == false) + { + window.Width = this.savedWindowWidth; + window.Height = this.savedWindowHeight; + } + } + + // Uncollapse elements + foreach (var element in this.collapsedElements) + { + element.Key.Visibility = element.Value; + } + + this.collapsedElements.Clear(); + } + + // Finds underlying ribbon control + private Ribbon FindRibbon() + { + DependencyObject item = this; + + while (item != null + && !(item is Ribbon)) + { + item = VisualTreeHelper.GetParent(item); + } + + return (Ribbon)item; + } + + private void SaveWindowMinSize(Window window) + { + if (window == null) + { + this.savedWindowMinWidth = double.NaN; + this.savedWindowMinHeight = double.NaN; + return; + } + + this.savedWindowMinWidth = window.MinWidth; + this.savedWindowMinHeight = window.MinHeight; + } + + private void SaveWindowSize(Window window) + { + if (window == null) + { + this.savedWindowWidth = double.NaN; + this.savedWindowHeight = double.NaN; + return; + } + + this.savedWindowWidth = window.ActualWidth; + this.savedWindowHeight = window.ActualHeight; + } + + private void OnWindowSizeChanged(object sender, SizeChangedEventArgs e) + { + this.SaveWindowSize(Window.GetWindow(this)); + } + + private void OnTabControlRequestBackstageClose(object sender, EventArgs e) + { + this.IsOpen = false; + } + + // We have to collapse WindowsFormsHost while Backstage is open + private void CollapseWindowsFormsHosts(DependencyObject parent) + { + if (parent == null) + { + return; + } + + var frameworkElement = parent as FrameworkElement; + + // Do not hide contents in the backstage area + if (parent is BackstageAdorner) return; + + if (frameworkElement != null) + { + if ((parent is HwndHost) && + frameworkElement.Visibility != Visibility.Collapsed) + { + this.collapsedElements.Add(frameworkElement, frameworkElement.Visibility); + frameworkElement.Visibility = Visibility.Collapsed; + return; + } + } + + // Traverse visual tree + for (var i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++) + { + this.CollapseWindowsFormsHosts(VisualTreeHelper.GetChild(parent, i)); + } + } + + /// + /// Invoked when an unhandled attached event reaches an element in its route that is derived from this class. Implement this method to add class handling for this event. + /// + /// The that contains the event data. + protected override void OnKeyDown(KeyEventArgs e) + { + if (e.Handled) + { + return; + } + + switch (e.Key) + { + case Key.Enter: + case Key.Space: + if (this.IsFocused) + { + this.IsOpen = !this.IsOpen; + e.Handled = true; + } + break; + } + + base.OnKeyDown(e); + } + + // Handles backstage Esc key keydown + private void HandleWindowKeyDown(object sender, KeyEventArgs e) + { + if (this.CloseOnEsc && e.Key == Key.Escape) + { + // only handle ESC when the backstage is open + e.Handled = this.IsOpen; + + this.IsOpen = false; + } + } + + private void OnBackstageLoaded(object sender, RoutedEventArgs e) + { + this.AddHandler(PopupService.DismissPopupEvent, (DismissPopupEventHandler)this.OnPopupDismiss); + } + + private void OnBackstageUnloaded(object sender, RoutedEventArgs e) + { + this.RemoveHandler(PopupService.DismissPopupEvent, (DismissPopupEventHandler)this.OnPopupDismiss); + } + + #endregion + + #endregion + + #region Overrides + + /// + /// Invoked when an unhandled System.Windows.UIElement.PreviewMouseLeftButtonDownrouted event reaches an element + /// in its route that is derived from this class. Implement this method to add class handling for this event. + /// + /// The System.Windows.Input.MouseButtonEventArgs that contains the event data. + /// The event data reports that the left mouse button was pressed. + protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) + { + this.Click(); + } + + /// + /// Handles key tip pressed + /// + public override void OnKeyTipPressed() + { + this.IsOpen = true; + base.OnKeyTipPressed(); + } + + /// + /// Handles back navigation with KeyTips + /// + public override void OnKeyTipBack() + { + this.IsOpen = false; + base.OnKeyTipBack(); + } + + /// + /// When overridden in a derived class, is invoked whenever application code or internal processes call . + /// + public override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + if (this.IsOpen) + { + this.Hide(); + } + + this.DestroyAdorner(); + + if (this.IsOpen) + { + this.Show(); + } + } + + #endregion + + #region Quick Access Toolbar + + /// + /// Gets control which represents shortcut item. + /// This item MUST be syncronized with the original + /// and send command to original one control. + /// + /// Control which represents shortcut item + public override FrameworkElement CreateQuickAccessItem() + { + throw new NotImplementedException(); + } + + #endregion + } } \ No newline at end of file diff --git a/Fluent/Controls/BackstageAdorner.cs b/Fluent.Ribbon/Controls/BackstageAdorner.cs similarity index 97% rename from Fluent/Controls/BackstageAdorner.cs rename to Fluent.Ribbon/Controls/BackstageAdorner.cs index 280207776..0feef5814 100644 --- a/Fluent/Controls/BackstageAdorner.cs +++ b/Fluent.Ribbon/Controls/BackstageAdorner.cs @@ -1,114 +1,114 @@ -using System; -using System.Windows; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; - -namespace Fluent -{ - /// - /// Represents adorner for Backstage - /// - internal class BackstageAdorner : Adorner - { - // Backstage - private readonly Backstage backstage; - - // Content of Backstage - private readonly UIElement backstageContent; - - // Collection of visual children - private readonly VisualCollection visualChildren; - - /// - /// Constructor - /// - /// Adorned element - /// Backstage - public BackstageAdorner(FrameworkElement adornedElement, Backstage backstage) - : base(adornedElement) - { - KeyboardNavigation.SetTabNavigation(this, KeyboardNavigationMode.Cycle); - - this.backstage = backstage; - this.backstageContent = this.backstage.Content; - - this.visualChildren = new VisualCollection(this) - { - this.backstageContent - }; - - // TODO: fix it! (below ugly workaround) in measureoverride we cannot get RenderSize, we must use DesiredSize - // Syncronize with visual size - this.Loaded += this.OnLoaded; - this.Unloaded += this.OnUnloaded; - } - - private void OnLoaded(object sender, RoutedEventArgs e) - { - CompositionTarget.Rendering += this.CompositionTargetRendering; - } - - private void OnUnloaded(object sender, RoutedEventArgs e) - { - CompositionTarget.Rendering -= this.CompositionTargetRendering; - } - - private void CompositionTargetRendering(object sender, EventArgs e) - { - if (this.RenderSize != this.AdornedElement.RenderSize) - { - this.InvalidateMeasure(); - } - } - - public void Clear() - { - this.visualChildren.Clear(); - } - - #region Layout & Visual Children - - /// - /// Positions child elements and determines - /// a size for the control - /// - /// The final area within the parent - /// that this element should use to arrange - /// itself and its children - /// The actual size used - protected override Size ArrangeOverride(Size finalSize) - { - this.backstageContent.Arrange(new Rect(0, 0, finalSize.Width, Math.Max(0, finalSize.Height))); - return finalSize; - } - - /// - /// Measures KeyTips - /// - /// The available size that this element can give to child elements. - /// The size that the groups container determines it needs during - /// layout, based on its calculations of child element sizes. - /// - protected override Size MeasureOverride(Size constraint) - { - // TODO: fix it! (below ugly workaround) in measureoverride we cannot get RenderSize, we must use DesiredSize - this.backstageContent.Measure(new Size(this.AdornedElement.RenderSize.Width, Math.Max(0, this.AdornedElement.RenderSize.Height))); - return this.AdornedElement.RenderSize; - } - - /// - /// Gets visual children count - /// - protected override int VisualChildrenCount { get { return this.visualChildren.Count; } } - - /// - /// Returns a child at the specified index from a collection of child elements - /// - /// The zero-based index of the requested child element in the collection - /// The requested child element - protected override Visual GetVisualChild(int index) { return this.visualChildren[index]; } - - #endregion - } +using System; +using System.Windows; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; + +namespace Fluent +{ + /// + /// Represents adorner for Backstage + /// + internal class BackstageAdorner : Adorner + { + // Backstage + private readonly Backstage backstage; + + // Content of Backstage + private readonly UIElement backstageContent; + + // Collection of visual children + private readonly VisualCollection visualChildren; + + /// + /// Constructor + /// + /// Adorned element + /// Backstage + public BackstageAdorner(FrameworkElement adornedElement, Backstage backstage) + : base(adornedElement) + { + KeyboardNavigation.SetTabNavigation(this, KeyboardNavigationMode.Cycle); + + this.backstage = backstage; + this.backstageContent = this.backstage.Content; + + this.visualChildren = new VisualCollection(this) + { + this.backstageContent + }; + + // TODO: fix it! (below ugly workaround) in measureoverride we cannot get RenderSize, we must use DesiredSize + // Syncronize with visual size + this.Loaded += this.OnLoaded; + this.Unloaded += this.OnUnloaded; + } + + private void OnLoaded(object sender, RoutedEventArgs e) + { + CompositionTarget.Rendering += this.CompositionTargetRendering; + } + + private void OnUnloaded(object sender, RoutedEventArgs e) + { + CompositionTarget.Rendering -= this.CompositionTargetRendering; + } + + private void CompositionTargetRendering(object sender, EventArgs e) + { + if (this.RenderSize != this.AdornedElement.RenderSize) + { + this.InvalidateMeasure(); + } + } + + public void Clear() + { + this.visualChildren.Clear(); + } + + #region Layout & Visual Children + + /// + /// Positions child elements and determines + /// a size for the control + /// + /// The final area within the parent + /// that this element should use to arrange + /// itself and its children + /// The actual size used + protected override Size ArrangeOverride(Size finalSize) + { + this.backstageContent.Arrange(new Rect(0, 0, finalSize.Width, Math.Max(0, finalSize.Height))); + return finalSize; + } + + /// + /// Measures KeyTips + /// + /// The available size that this element can give to child elements. + /// The size that the groups container determines it needs during + /// layout, based on its calculations of child element sizes. + /// + protected override Size MeasureOverride(Size constraint) + { + // TODO: fix it! (below ugly workaround) in measureoverride we cannot get RenderSize, we must use DesiredSize + this.backstageContent.Measure(new Size(this.AdornedElement.RenderSize.Width, Math.Max(0, this.AdornedElement.RenderSize.Height))); + return this.AdornedElement.RenderSize; + } + + /// + /// Gets visual children count + /// + protected override int VisualChildrenCount { get { return this.visualChildren.Count; } } + + /// + /// Returns a child at the specified index from a collection of child elements + /// + /// The zero-based index of the requested child element in the collection + /// The requested child element + protected override Visual GetVisualChild(int index) { return this.visualChildren[index]; } + + #endregion + } } \ No newline at end of file diff --git a/Fluent/Controls/BackstageTabControl.cs b/Fluent.Ribbon/Controls/BackstageTabControl.cs similarity index 97% rename from Fluent/Controls/BackstageTabControl.cs rename to Fluent.Ribbon/Controls/BackstageTabControl.cs index 1630073ac..440481acf 100644 --- a/Fluent/Controls/BackstageTabControl.cs +++ b/Fluent.Ribbon/Controls/BackstageTabControl.cs @@ -1,442 +1,442 @@ -using System; -using System.Collections.Specialized; -using System.ComponentModel; -using System.Diagnostics.CodeAnalysis; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Controls.Primitives; -using System.Windows.Input; -using System.Windows.Media; - -namespace Fluent -{ - /// - /// Represents Backstage tab control. - /// - public class BackstageTabControl : Selector - { - #region Properties - - // Dependency property key for SelectedContent - private static readonly DependencyPropertyKey SelectedContentPropertyKey = DependencyProperty.RegisterReadOnly("SelectedContent", typeof(object), typeof(BackstageTabControl), new FrameworkPropertyMetadata(null)); - - /// - /// Dependency property SelectedContent - /// - public static readonly DependencyProperty SelectedContentProperty = SelectedContentPropertyKey.DependencyProperty; - - /// - /// Gets content for selected tab - /// - [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] - public object SelectedContent - { - get - { - return this.GetValue(SelectedContentProperty); - } - internal set - { - this.SetValue(SelectedContentPropertyKey, value); - } - } - - /// - /// Dependency property ContentStringFormat - /// - public static readonly DependencyProperty ContentStringFormatProperty = DependencyProperty.Register("ContentStringFormat", typeof(string), typeof(BackstageTabControl), new FrameworkPropertyMetadata(null)); - - /// - /// Dependency property ContentTemplate - /// - public static readonly DependencyProperty ContentTemplateProperty = DependencyProperty.Register("ContentTemplate", typeof(DataTemplate), typeof(BackstageTabControl), new FrameworkPropertyMetadata(null)); - - /// - /// Dependency property ContentTemplateSelector - /// - public static readonly DependencyProperty ContentTemplateSelectorProperty = DependencyProperty.Register("ContentTemplateSelector", typeof(DataTemplateSelector), typeof(BackstageTabControl), new FrameworkPropertyMetadata(null)); - - private static readonly DependencyPropertyKey SelectedContentStringFormatPropertyKey = DependencyProperty.RegisterReadOnly("SelectedContentStringFormat", typeof(string), typeof(BackstageTabControl), new FrameworkPropertyMetadata(null)); - - /// - /// Dependency property SelectedContentStringFormat - /// - public static readonly DependencyProperty SelectedContentStringFormatProperty = SelectedContentStringFormatPropertyKey.DependencyProperty; - - private static readonly DependencyPropertyKey SelectedContentTemplatePropertyKey = DependencyProperty.RegisterReadOnly("SelectedContentTemplate", typeof(DataTemplate), typeof(BackstageTabControl), new FrameworkPropertyMetadata(null)); - - /// - /// Dependency property SelectedContentTemplate - /// - public static readonly DependencyProperty SelectedContentTemplateProperty = SelectedContentTemplatePropertyKey.DependencyProperty; - - private static readonly DependencyPropertyKey SelectedContentTemplateSelectorPropertyKey = DependencyProperty.RegisterReadOnly("SelectedContentTemplateSelector", typeof(DataTemplateSelector), typeof(BackstageTabControl), new FrameworkPropertyMetadata(null)); - - /// - /// Dependency property SelectedContentTemplateSelector - /// - public static readonly DependencyProperty SelectedContentTemplateSelectorProperty = SelectedContentTemplateSelectorPropertyKey.DependencyProperty; - - /// - /// Get or sets the string format for the content. - /// - public string ContentStringFormat - { - get - { - return (string)this.GetValue(ContentStringFormatProperty); - } - set - { - this.SetValue(ContentStringFormatProperty, value); - } - } - - /// - /// Gets or sets the which should be used for the content - /// - public DataTemplate ContentTemplate - { - get - { - return (DataTemplate)this.GetValue(ContentTemplateProperty); - } - set - { - this.SetValue(ContentTemplateProperty, value); - } - } - - /// - /// Gets or sets the which should be used for the content - /// - public DataTemplateSelector ContentTemplateSelector - { - get - { - return (DataTemplateSelector)this.GetValue(ContentTemplateSelectorProperty); - } - set - { - this.SetValue(ContentTemplateSelectorProperty, value); - } - } - - /// - /// Get or sets the string format for the selected content. - /// - public string SelectedContentStringFormat - { - get - { - return (string)this.GetValue(SelectedContentStringFormatProperty); - } - internal set - { - this.SetValue(SelectedContentStringFormatPropertyKey, value); - } - } - - /// - /// Gets or sets the which should be used for the selected content - /// - [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] - public DataTemplate SelectedContentTemplate - { - get - { - return (DataTemplate)this.GetValue(SelectedContentTemplateProperty); - } - internal set - { - this.SetValue(SelectedContentTemplatePropertyKey, value); - } - } - - /// - /// Gets or sets the which should be used for the selected content - /// - [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] - public DataTemplateSelector SelectedContentTemplateSelector - { - get - { - return (DataTemplateSelector)this.GetValue(SelectedContentTemplateSelectorProperty); - } - internal set - { - this.SetValue(SelectedContentTemplateSelectorPropertyKey, value); - } - } - - #region ItemsPanelBackground - - /// - /// Gets or sets current Backround of the ItemsPanel - /// - public Brush ItemsPanelBackground - { - get { return (Brush)this.GetValue(ItemsPanelBackgroundProperty); } - set { this.SetValue(ItemsPanelBackgroundProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Foreground. - /// This enables animation, styling, binding, etc... - /// - public static DependencyProperty ItemsPanelBackgroundProperty = - DependencyProperty.Register("ItemsPanelBackground", typeof(Brush), typeof(BackstageTabControl)); - - #endregion - - #endregion - - #region Constructors - - /// - /// Static constructor - /// - [SuppressMessage("Microsoft.Performance", "CA1810")] - static BackstageTabControl() - { - DefaultStyleKeyProperty.OverrideMetadata(typeof(BackstageTabControl), new FrameworkPropertyMetadata(typeof(BackstageTabControl))); - StyleProperty.OverrideMetadata(typeof(BackstageTabControl), new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceStyle))); - } - - // Coerce object style - static object OnCoerceStyle(DependencyObject d, object basevalue) - { - if (basevalue == null) - { - basevalue = (d as FrameworkElement).TryFindResource(typeof(BackstageTabControl)); - } - - return basevalue; - } - - /// - /// Default constructor - /// - public BackstageTabControl() - { - this.Loaded += this.OnLoaded; - this.Unloaded += this.OnUnloaded; - - // Fixed incoreect menu showing - this.ContextMenu = new ContextMenu - { - Width = 0, - Height = 0, - HasDropShadow = false - }; - this.ContextMenu.Opened += delegate { ContextMenu.IsOpen = false; }; - } - - #endregion - - #region Overrides - - /// - /// Raises the System.Windows.FrameworkElement.Initialized event. - /// This method is invoked whenever System.Windows.FrameworkElement. - /// IsInitialized is set to true internally. - /// - /// The System.Windows.RoutedEventArgs that contains the event data. - protected override void OnInitialized(EventArgs e) - { - base.OnInitialized(e); - this.ItemContainerGenerator.StatusChanged += this.OnGeneratorStatusChanged; - } - - /// - /// Creates or identifies the element that is used to display the given item. - /// - /// The element that is used to display the given item. - protected override DependencyObject GetContainerForItemOverride() - { - return new BackstageTabItem(); - } - - /// - /// Determines if the specified item is (or is eligible to be) its own container. - /// - /// The item to check. - /// - protected override bool IsItemItsOwnContainerOverride(object item) - { - return item is BackstageTabItem - || item is Button - || item is SeparatorTabItem - || item is Separator; - } - - /// - /// Updates the current selection when an item in the System.Windows.Controls.Primitives.Selector has changed - /// - /// The event data. - protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e) - { - base.OnItemsChanged(e); - - if ((e.Action == NotifyCollectionChangedAction.Remove) && (this.SelectedIndex == -1)) - { - var startIndex = e.OldStartingIndex + 1; - if (startIndex > this.Items.Count) - { - startIndex = 0; - } - - var item = this.FindNextTabItem(startIndex, -1); - if (item != null) - { - item.IsSelected = true; - } - } - } - - /// - /// Called when the selection changes. - /// - /// The event data. - protected override void OnSelectionChanged(SelectionChangedEventArgs e) - { - base.OnSelectionChanged(e); - if (e.AddedItems.Count > 0) - { - this.UpdateSelectedContent(); - } - e.Handled = true; - } - - /// - /// Invoked when an unhandled MouseLeftButtonDown routed event - /// is raised on this element. Implement this method to add class handling for this event. - /// - /// The MouseButtonEventArgs that contains the event data. - /// The event data reports that the left mouse button was pressed - protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) - { - base.OnMouseLeftButtonDown(e); - e.Handled = true; - } - - #endregion - - #region Private methods - - // Gets selected ribbon tab item - BackstageTabItem GetSelectedTabItem() - { - object selectedItem = this.SelectedItem; - if (selectedItem == null) - { - return null; - } - BackstageTabItem item = selectedItem as BackstageTabItem; - if (item == null) - { - item = this.FindNextTabItem(this.SelectedIndex, 1); - this.SelectedItem = item; - } - return item; - } - - // Finds next tab item - private BackstageTabItem FindNextTabItem(int startIndex, int direction) - { - if (direction != 0) - { - int index = startIndex; - for (int i = 0; i < this.Items.Count; i++) - { - index += direction; - if (index >= this.Items.Count) - { - index = 0; - } - else if (index < 0) - { - index = this.Items.Count - 1; - } - BackstageTabItem item2 = this.ItemContainerGenerator.ContainerFromIndex(index) as BackstageTabItem; - if (((item2 != null) && item2.IsEnabled) && (item2.Visibility == Visibility.Visible)) - { - return item2; - } - } - } - return null; - } - - // Updates selected content - private void UpdateSelectedContent() - { - if (this.SelectedIndex < 0) - { - - this.SelectedContent = null; - } - else - { - BackstageTabItem selectedTabItem = this.GetSelectedTabItem(); - if (selectedTabItem != null) - { - this.SelectedContent = selectedTabItem.Content; - if (((selectedTabItem.ContentTemplate != null) || (selectedTabItem.ContentTemplateSelector != null)) || (selectedTabItem.ContentStringFormat != null)) - { - this.SelectedContentTemplate = selectedTabItem.ContentTemplate; - this.SelectedContentTemplateSelector = selectedTabItem.ContentTemplateSelector; - this.SelectedContentStringFormat = selectedTabItem.ContentStringFormat; - } - else - { - this.SelectedContentTemplate = this.ContentTemplate; - this.SelectedContentTemplateSelector = this.ContentTemplateSelector; - this.SelectedContentStringFormat = this.ContentStringFormat; - } - - this.UpdateLayout(); - } - } - } - - #endregion - - #region Event handling - - // Handles GeneratorStatusChange - private void OnGeneratorStatusChanged(object sender, EventArgs e) - { - if (this.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated) - { - if (this.HasItems && (this.SelectedIndex == -1)) - { - this.SelectedIndex = 0; - } - this.UpdateSelectedContent(); - } - } - - private void OnPopupDismiss(object sender, DismissPopupEventArgs e) - { - var backstage = LogicalTreeHelper.GetParent(this); - - if (backstage != null) - { - PopupService.RaiseDismissPopupEvent(backstage, DismissPopupMode.Always); - } - } - - private void OnLoaded(object sender, RoutedEventArgs e) - { - this.AddHandler(PopupService.DismissPopupEvent, (DismissPopupEventHandler)this.OnPopupDismiss); - } - - private void OnUnloaded(object sender, RoutedEventArgs e) - { - this.RemoveHandler(PopupService.DismissPopupEvent, (DismissPopupEventHandler)this.OnPopupDismiss); - } - - #endregion - } +using System; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Input; +using System.Windows.Media; + +namespace Fluent +{ + /// + /// Represents Backstage tab control. + /// + public class BackstageTabControl : Selector + { + #region Properties + + // Dependency property key for SelectedContent + private static readonly DependencyPropertyKey SelectedContentPropertyKey = DependencyProperty.RegisterReadOnly("SelectedContent", typeof(object), typeof(BackstageTabControl), new FrameworkPropertyMetadata(null)); + + /// + /// Dependency property SelectedContent + /// + public static readonly DependencyProperty SelectedContentProperty = SelectedContentPropertyKey.DependencyProperty; + + /// + /// Gets content for selected tab + /// + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public object SelectedContent + { + get + { + return this.GetValue(SelectedContentProperty); + } + internal set + { + this.SetValue(SelectedContentPropertyKey, value); + } + } + + /// + /// Dependency property ContentStringFormat + /// + public static readonly DependencyProperty ContentStringFormatProperty = DependencyProperty.Register("ContentStringFormat", typeof(string), typeof(BackstageTabControl), new FrameworkPropertyMetadata(null)); + + /// + /// Dependency property ContentTemplate + /// + public static readonly DependencyProperty ContentTemplateProperty = DependencyProperty.Register("ContentTemplate", typeof(DataTemplate), typeof(BackstageTabControl), new FrameworkPropertyMetadata(null)); + + /// + /// Dependency property ContentTemplateSelector + /// + public static readonly DependencyProperty ContentTemplateSelectorProperty = DependencyProperty.Register("ContentTemplateSelector", typeof(DataTemplateSelector), typeof(BackstageTabControl), new FrameworkPropertyMetadata(null)); + + private static readonly DependencyPropertyKey SelectedContentStringFormatPropertyKey = DependencyProperty.RegisterReadOnly("SelectedContentStringFormat", typeof(string), typeof(BackstageTabControl), new FrameworkPropertyMetadata(null)); + + /// + /// Dependency property SelectedContentStringFormat + /// + public static readonly DependencyProperty SelectedContentStringFormatProperty = SelectedContentStringFormatPropertyKey.DependencyProperty; + + private static readonly DependencyPropertyKey SelectedContentTemplatePropertyKey = DependencyProperty.RegisterReadOnly("SelectedContentTemplate", typeof(DataTemplate), typeof(BackstageTabControl), new FrameworkPropertyMetadata(null)); + + /// + /// Dependency property SelectedContentTemplate + /// + public static readonly DependencyProperty SelectedContentTemplateProperty = SelectedContentTemplatePropertyKey.DependencyProperty; + + private static readonly DependencyPropertyKey SelectedContentTemplateSelectorPropertyKey = DependencyProperty.RegisterReadOnly("SelectedContentTemplateSelector", typeof(DataTemplateSelector), typeof(BackstageTabControl), new FrameworkPropertyMetadata(null)); + + /// + /// Dependency property SelectedContentTemplateSelector + /// + public static readonly DependencyProperty SelectedContentTemplateSelectorProperty = SelectedContentTemplateSelectorPropertyKey.DependencyProperty; + + /// + /// Get or sets the string format for the content. + /// + public string ContentStringFormat + { + get + { + return (string)this.GetValue(ContentStringFormatProperty); + } + set + { + this.SetValue(ContentStringFormatProperty, value); + } + } + + /// + /// Gets or sets the which should be used for the content + /// + public DataTemplate ContentTemplate + { + get + { + return (DataTemplate)this.GetValue(ContentTemplateProperty); + } + set + { + this.SetValue(ContentTemplateProperty, value); + } + } + + /// + /// Gets or sets the which should be used for the content + /// + public DataTemplateSelector ContentTemplateSelector + { + get + { + return (DataTemplateSelector)this.GetValue(ContentTemplateSelectorProperty); + } + set + { + this.SetValue(ContentTemplateSelectorProperty, value); + } + } + + /// + /// Get or sets the string format for the selected content. + /// + public string SelectedContentStringFormat + { + get + { + return (string)this.GetValue(SelectedContentStringFormatProperty); + } + internal set + { + this.SetValue(SelectedContentStringFormatPropertyKey, value); + } + } + + /// + /// Gets or sets the which should be used for the selected content + /// + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public DataTemplate SelectedContentTemplate + { + get + { + return (DataTemplate)this.GetValue(SelectedContentTemplateProperty); + } + internal set + { + this.SetValue(SelectedContentTemplatePropertyKey, value); + } + } + + /// + /// Gets or sets the which should be used for the selected content + /// + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public DataTemplateSelector SelectedContentTemplateSelector + { + get + { + return (DataTemplateSelector)this.GetValue(SelectedContentTemplateSelectorProperty); + } + internal set + { + this.SetValue(SelectedContentTemplateSelectorPropertyKey, value); + } + } + + #region ItemsPanelBackground + + /// + /// Gets or sets current Backround of the ItemsPanel + /// + public Brush ItemsPanelBackground + { + get { return (Brush)this.GetValue(ItemsPanelBackgroundProperty); } + set { this.SetValue(ItemsPanelBackgroundProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Foreground. + /// This enables animation, styling, binding, etc... + /// + public static DependencyProperty ItemsPanelBackgroundProperty = + DependencyProperty.Register("ItemsPanelBackground", typeof(Brush), typeof(BackstageTabControl)); + + #endregion + + #endregion + + #region Constructors + + /// + /// Static constructor + /// + [SuppressMessage("Microsoft.Performance", "CA1810")] + static BackstageTabControl() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(BackstageTabControl), new FrameworkPropertyMetadata(typeof(BackstageTabControl))); + StyleProperty.OverrideMetadata(typeof(BackstageTabControl), new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceStyle))); + } + + // Coerce object style + static object OnCoerceStyle(DependencyObject d, object basevalue) + { + if (basevalue == null) + { + basevalue = (d as FrameworkElement).TryFindResource(typeof(BackstageTabControl)); + } + + return basevalue; + } + + /// + /// Default constructor + /// + public BackstageTabControl() + { + this.Loaded += this.OnLoaded; + this.Unloaded += this.OnUnloaded; + + // Fixed incoreect menu showing + this.ContextMenu = new ContextMenu + { + Width = 0, + Height = 0, + HasDropShadow = false + }; + this.ContextMenu.Opened += delegate { ContextMenu.IsOpen = false; }; + } + + #endregion + + #region Overrides + + /// + /// Raises the System.Windows.FrameworkElement.Initialized event. + /// This method is invoked whenever System.Windows.FrameworkElement. + /// IsInitialized is set to true internally. + /// + /// The System.Windows.RoutedEventArgs that contains the event data. + protected override void OnInitialized(EventArgs e) + { + base.OnInitialized(e); + this.ItemContainerGenerator.StatusChanged += this.OnGeneratorStatusChanged; + } + + /// + /// Creates or identifies the element that is used to display the given item. + /// + /// The element that is used to display the given item. + protected override DependencyObject GetContainerForItemOverride() + { + return new BackstageTabItem(); + } + + /// + /// Determines if the specified item is (or is eligible to be) its own container. + /// + /// The item to check. + /// + protected override bool IsItemItsOwnContainerOverride(object item) + { + return item is BackstageTabItem + || item is Button + || item is SeparatorTabItem + || item is Separator; + } + + /// + /// Updates the current selection when an item in the System.Windows.Controls.Primitives.Selector has changed + /// + /// The event data. + protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e) + { + base.OnItemsChanged(e); + + if ((e.Action == NotifyCollectionChangedAction.Remove) && (this.SelectedIndex == -1)) + { + var startIndex = e.OldStartingIndex + 1; + if (startIndex > this.Items.Count) + { + startIndex = 0; + } + + var item = this.FindNextTabItem(startIndex, -1); + if (item != null) + { + item.IsSelected = true; + } + } + } + + /// + /// Called when the selection changes. + /// + /// The event data. + protected override void OnSelectionChanged(SelectionChangedEventArgs e) + { + base.OnSelectionChanged(e); + if (e.AddedItems.Count > 0) + { + this.UpdateSelectedContent(); + } + e.Handled = true; + } + + /// + /// Invoked when an unhandled MouseLeftButtonDown routed event + /// is raised on this element. Implement this method to add class handling for this event. + /// + /// The MouseButtonEventArgs that contains the event data. + /// The event data reports that the left mouse button was pressed + protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) + { + base.OnMouseLeftButtonDown(e); + e.Handled = true; + } + + #endregion + + #region Private methods + + // Gets selected ribbon tab item + BackstageTabItem GetSelectedTabItem() + { + object selectedItem = this.SelectedItem; + if (selectedItem == null) + { + return null; + } + BackstageTabItem item = selectedItem as BackstageTabItem; + if (item == null) + { + item = this.FindNextTabItem(this.SelectedIndex, 1); + this.SelectedItem = item; + } + return item; + } + + // Finds next tab item + private BackstageTabItem FindNextTabItem(int startIndex, int direction) + { + if (direction != 0) + { + int index = startIndex; + for (int i = 0; i < this.Items.Count; i++) + { + index += direction; + if (index >= this.Items.Count) + { + index = 0; + } + else if (index < 0) + { + index = this.Items.Count - 1; + } + BackstageTabItem item2 = this.ItemContainerGenerator.ContainerFromIndex(index) as BackstageTabItem; + if (((item2 != null) && item2.IsEnabled) && (item2.Visibility == Visibility.Visible)) + { + return item2; + } + } + } + return null; + } + + // Updates selected content + private void UpdateSelectedContent() + { + if (this.SelectedIndex < 0) + { + + this.SelectedContent = null; + } + else + { + BackstageTabItem selectedTabItem = this.GetSelectedTabItem(); + if (selectedTabItem != null) + { + this.SelectedContent = selectedTabItem.Content; + if (((selectedTabItem.ContentTemplate != null) || (selectedTabItem.ContentTemplateSelector != null)) || (selectedTabItem.ContentStringFormat != null)) + { + this.SelectedContentTemplate = selectedTabItem.ContentTemplate; + this.SelectedContentTemplateSelector = selectedTabItem.ContentTemplateSelector; + this.SelectedContentStringFormat = selectedTabItem.ContentStringFormat; + } + else + { + this.SelectedContentTemplate = this.ContentTemplate; + this.SelectedContentTemplateSelector = this.ContentTemplateSelector; + this.SelectedContentStringFormat = this.ContentStringFormat; + } + + this.UpdateLayout(); + } + } + } + + #endregion + + #region Event handling + + // Handles GeneratorStatusChange + private void OnGeneratorStatusChanged(object sender, EventArgs e) + { + if (this.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated) + { + if (this.HasItems && (this.SelectedIndex == -1)) + { + this.SelectedIndex = 0; + } + this.UpdateSelectedContent(); + } + } + + private void OnPopupDismiss(object sender, DismissPopupEventArgs e) + { + var backstage = LogicalTreeHelper.GetParent(this); + + if (backstage != null) + { + PopupService.RaiseDismissPopupEvent(backstage, DismissPopupMode.Always); + } + } + + private void OnLoaded(object sender, RoutedEventArgs e) + { + this.AddHandler(PopupService.DismissPopupEvent, (DismissPopupEventHandler)this.OnPopupDismiss); + } + + private void OnUnloaded(object sender, RoutedEventArgs e) + { + this.RemoveHandler(PopupService.DismissPopupEvent, (DismissPopupEventHandler)this.OnPopupDismiss); + } + + #endregion + } } \ No newline at end of file diff --git a/Fluent/Controls/BackstageTabItem.cs b/Fluent.Ribbon/Controls/BackstageTabItem.cs similarity index 97% rename from Fluent/Controls/BackstageTabItem.cs rename to Fluent.Ribbon/Controls/BackstageTabItem.cs index bb4ee0600..f36d8e6f8 100644 --- a/Fluent/Controls/BackstageTabItem.cs +++ b/Fluent.Ribbon/Controls/BackstageTabItem.cs @@ -1,239 +1,239 @@ -using System.ComponentModel; -using System.Diagnostics.CodeAnalysis; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Controls.Primitives; -using System.Windows.Input; - -namespace Fluent -{ - /// - /// Represents backstage tab item - /// - public class BackstageTabItem : ContentControl, IKeyTipedControl - { - #region Properties - - #region KeyTip - - /// - /// Gets or sets KeyTip for element. - /// - public string KeyTip - { - get { return (string)this.GetValue(KeyTipProperty); } - set { this.SetValue(KeyTipProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Keys. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty KeyTipProperty = Fluent.KeyTip.KeysProperty.AddOwner(typeof(BackstageTabItem)); - - #endregion - - /// - /// Dependency property for isSelected - /// - public static readonly DependencyProperty IsSelectedProperty = - Selector.IsSelectedProperty.AddOwner(typeof(BackstageTabItem), - new FrameworkPropertyMetadata(false, - FrameworkPropertyMetadataOptions.Journal | - FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | - FrameworkPropertyMetadataOptions.AffectsParentMeasure, - OnIsSelectedChanged)); - - /// - /// Gets or sets whether the tab is selected - /// - [Bindable(true), Category("Appearance")] - public bool IsSelected - { - get - { - return (bool)this.GetValue(IsSelectedProperty); - } - set - { - this.SetValue(IsSelectedProperty, value); - } - } - - /// - /// Gets parent tab control - /// - internal BackstageTabControl TabControlParent - { - get - { - return (ItemsControl.ItemsControlFromItemContainer(this) as BackstageTabControl); - } - } - - /// - /// Gets or sets tab items text - /// - public object Header - { - get { return this.GetValue(HeaderProperty); } - set { this.SetValue(HeaderProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Text. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register("Header", typeof(object), typeof(BackstageTabItem), new PropertyMetadata(null)); - - #endregion - - #region Constructors - - /// - /// Static constructor - /// - [SuppressMessage("Microsoft.Performance", "CA1810")] - static BackstageTabItem() - { - DefaultStyleKeyProperty.OverrideMetadata(typeof(BackstageTabItem), - new FrameworkPropertyMetadata(typeof(BackstageTabItem))); - StyleProperty.OverrideMetadata(typeof(BackstageTabItem), new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceStyle))); - } - - // Coerce object style - static object OnCoerceStyle(DependencyObject d, object basevalue) - { - if (basevalue == null) - { - basevalue = (d as FrameworkElement).TryFindResource(typeof(BackstageTabItem)); - } - - return basevalue; - } - - /// - /// Default constructor - /// - public BackstageTabItem() - { - - } - - #endregion - - #region Overrides - - /// - /// Called when the System.Windows.Controls.ContentControl.Content property changes. - /// - /// The old value of the System.Windows.Controls.ContentControl.Content property. - /// The new value of the System.Windows.Controls.ContentControl.Content property. - protected override void OnContentChanged(object oldContent, object newContent) - { - base.OnContentChanged(oldContent, newContent); - if (this.IsSelected && - this.TabControlParent != null) - { - this.TabControlParent.SelectedContent = newContent; - } - } - - /// - /// Invoked when an unhandled System.Windows.UIElement.MouseLeftButtonDownrouted event is raised on this element. - /// Implement this method to add class handling for this event. - /// - /// The System.Windows.Input.MouseButtonEventArgs that contains the event data. - /// The event data reports that the left mouse button was pressed. - protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) - { - if (((e.Source == this) || !this.IsSelected)) - { - if (this.TabControlParent != null && - this.TabControlParent.SelectedItem is BackstageTabItem) - ((BackstageTabItem)this.TabControlParent.SelectedItem).IsSelected = false; - - this.IsSelected = true; - } - e.Handled = true; - } - - #endregion - - #region Private methods - - // Handles IsSelected changed - private static void OnIsSelectedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - BackstageTabItem container = (BackstageTabItem)d; - bool newValue = (bool)e.NewValue; - - if (newValue) - { - BackstageTabControl backstage = container.Parent as BackstageTabControl; - if ((backstage != null) && (backstage.SelectedItem != container)) - { - if (backstage.SelectedItem is BackstageTabItem) (backstage.SelectedItem as BackstageTabItem).IsSelected = false; - backstage.SelectedItem = container; - } - container.OnSelected(new RoutedEventArgs(Selector.SelectedEvent, container)); - } - else - { - container.OnUnselected(new RoutedEventArgs(Selector.UnselectedEvent, container)); - } - } - - /// - /// Handles selected event - /// - /// The event data. - protected virtual void OnSelected(RoutedEventArgs e) - { - this.HandleIsSelectedChanged(e); - } - - /// - /// Handles unselected event - /// - /// The event data. - protected virtual void OnUnselected(RoutedEventArgs e) - { - this.HandleIsSelectedChanged(e); - } - - #endregion - - #region Event handling - - /// - /// Handles IsSelected changed - /// - /// The event data. - private void HandleIsSelectedChanged(RoutedEventArgs e) - { - this.RaiseEvent(e); - } - - #endregion - - /// - /// Handles key tip pressed - /// - public void OnKeyTipPressed() - { - if (this.TabControlParent != null && - this.TabControlParent.SelectedItem is RibbonTabItem) - ((BackstageTabItem)this.TabControlParent.SelectedItem).IsSelected = false; - - this.IsSelected = true; - } - - /// - /// Handles back navigation with KeyTips - /// - public void OnKeyTipBack() - { - } - } -} +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Input; + +namespace Fluent +{ + /// + /// Represents backstage tab item + /// + public class BackstageTabItem : ContentControl, IKeyTipedControl + { + #region Properties + + #region KeyTip + + /// + /// Gets or sets KeyTip for element. + /// + public string KeyTip + { + get { return (string)this.GetValue(KeyTipProperty); } + set { this.SetValue(KeyTipProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Keys. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty KeyTipProperty = Fluent.KeyTip.KeysProperty.AddOwner(typeof(BackstageTabItem)); + + #endregion + + /// + /// Dependency property for isSelected + /// + public static readonly DependencyProperty IsSelectedProperty = + Selector.IsSelectedProperty.AddOwner(typeof(BackstageTabItem), + new FrameworkPropertyMetadata(false, + FrameworkPropertyMetadataOptions.Journal | + FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | + FrameworkPropertyMetadataOptions.AffectsParentMeasure, + OnIsSelectedChanged)); + + /// + /// Gets or sets whether the tab is selected + /// + [Bindable(true), Category("Appearance")] + public bool IsSelected + { + get + { + return (bool)this.GetValue(IsSelectedProperty); + } + set + { + this.SetValue(IsSelectedProperty, value); + } + } + + /// + /// Gets parent tab control + /// + internal BackstageTabControl TabControlParent + { + get + { + return (ItemsControl.ItemsControlFromItemContainer(this) as BackstageTabControl); + } + } + + /// + /// Gets or sets tab items text + /// + public object Header + { + get { return this.GetValue(HeaderProperty); } + set { this.SetValue(HeaderProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Text. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register("Header", typeof(object), typeof(BackstageTabItem), new PropertyMetadata(null)); + + #endregion + + #region Constructors + + /// + /// Static constructor + /// + [SuppressMessage("Microsoft.Performance", "CA1810")] + static BackstageTabItem() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(BackstageTabItem), + new FrameworkPropertyMetadata(typeof(BackstageTabItem))); + StyleProperty.OverrideMetadata(typeof(BackstageTabItem), new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceStyle))); + } + + // Coerce object style + static object OnCoerceStyle(DependencyObject d, object basevalue) + { + if (basevalue == null) + { + basevalue = (d as FrameworkElement).TryFindResource(typeof(BackstageTabItem)); + } + + return basevalue; + } + + /// + /// Default constructor + /// + public BackstageTabItem() + { + + } + + #endregion + + #region Overrides + + /// + /// Called when the System.Windows.Controls.ContentControl.Content property changes. + /// + /// The old value of the System.Windows.Controls.ContentControl.Content property. + /// The new value of the System.Windows.Controls.ContentControl.Content property. + protected override void OnContentChanged(object oldContent, object newContent) + { + base.OnContentChanged(oldContent, newContent); + if (this.IsSelected && + this.TabControlParent != null) + { + this.TabControlParent.SelectedContent = newContent; + } + } + + /// + /// Invoked when an unhandled System.Windows.UIElement.MouseLeftButtonDownrouted event is raised on this element. + /// Implement this method to add class handling for this event. + /// + /// The System.Windows.Input.MouseButtonEventArgs that contains the event data. + /// The event data reports that the left mouse button was pressed. + protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) + { + if (((e.Source == this) || !this.IsSelected)) + { + if (this.TabControlParent != null && + this.TabControlParent.SelectedItem is BackstageTabItem) + ((BackstageTabItem)this.TabControlParent.SelectedItem).IsSelected = false; + + this.IsSelected = true; + } + e.Handled = true; + } + + #endregion + + #region Private methods + + // Handles IsSelected changed + private static void OnIsSelectedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + BackstageTabItem container = (BackstageTabItem)d; + bool newValue = (bool)e.NewValue; + + if (newValue) + { + BackstageTabControl backstage = container.Parent as BackstageTabControl; + if ((backstage != null) && (backstage.SelectedItem != container)) + { + if (backstage.SelectedItem is BackstageTabItem) (backstage.SelectedItem as BackstageTabItem).IsSelected = false; + backstage.SelectedItem = container; + } + container.OnSelected(new RoutedEventArgs(Selector.SelectedEvent, container)); + } + else + { + container.OnUnselected(new RoutedEventArgs(Selector.UnselectedEvent, container)); + } + } + + /// + /// Handles selected event + /// + /// The event data. + protected virtual void OnSelected(RoutedEventArgs e) + { + this.HandleIsSelectedChanged(e); + } + + /// + /// Handles unselected event + /// + /// The event data. + protected virtual void OnUnselected(RoutedEventArgs e) + { + this.HandleIsSelectedChanged(e); + } + + #endregion + + #region Event handling + + /// + /// Handles IsSelected changed + /// + /// The event data. + private void HandleIsSelectedChanged(RoutedEventArgs e) + { + this.RaiseEvent(e); + } + + #endregion + + /// + /// Handles key tip pressed + /// + public void OnKeyTipPressed() + { + if (this.TabControlParent != null && + this.TabControlParent.SelectedItem is RibbonTabItem) + ((BackstageTabItem)this.TabControlParent.SelectedItem).IsSelected = false; + + this.IsSelected = true; + } + + /// + /// Handles back navigation with KeyTips + /// + public void OnKeyTipBack() + { + } + } +} diff --git a/Fluent/Controls/Button.cs b/Fluent.Ribbon/Controls/Button.cs similarity index 97% rename from Fluent/Controls/Button.cs rename to Fluent.Ribbon/Controls/Button.cs index 907ffb025..cdfb2313e 100644 --- a/Fluent/Controls/Button.cs +++ b/Fluent.Ribbon/Controls/Button.cs @@ -1,284 +1,284 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.Windows; -using System.Windows.Markup; - -namespace Fluent -{ - /// - /// Represents button - /// - [ContentProperty("Header")] - public class Button : System.Windows.Controls.Button, IRibbonControl, IQuickAccessItemProvider - { - #region Properties - - #region Size - - /// - /// Gets or sets Size for the element. - /// - public RibbonControlSize Size - { - get { return (RibbonControlSize)this.GetValue(SizeProperty); } - set { this.SetValue(SizeProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Size. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty SizeProperty = RibbonProperties.SizeProperty.AddOwner(typeof(Button)); - - #endregion - - #region SizeDefinition - - /// - /// Gets or sets SizeDefinition for element. - /// - public RibbonControlSizeDefinition SizeDefinition - { - get { return (RibbonControlSizeDefinition)this.GetValue(SizeDefinitionProperty); } - set { this.SetValue(SizeDefinitionProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for SizeDefinition. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty SizeDefinitionProperty = RibbonProperties.SizeDefinitionProperty.AddOwner(typeof(Button)); - - #endregion - - #region KeyTip - - /// - /// Gets or sets KeyTip for element. - /// - public string KeyTip - { - get { return (string)this.GetValue(KeyTipProperty); } - set { this.SetValue(KeyTipProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Keys. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty KeyTipProperty = Fluent.KeyTip.KeysProperty.AddOwner(typeof(Button)); - - #endregion - - #region Header - - /// - /// Gets or sets element Text - /// - public object Header - { - get { return (object)this.GetValue(HeaderProperty); } - set { this.SetValue(HeaderProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Header. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register("Header", typeof(object), typeof(Button), new PropertyMetadata(null)); - - #endregion - - #region Icon - - /// - /// Gets or sets Icon for the element - /// - public object Icon - { - get { return this.GetValue(IconProperty); } - set { this.SetValue(IconProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Icon. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IconProperty = RibbonControl.IconProperty.AddOwner(typeof(Button), new UIPropertyMetadata(null, OnIconChanged)); - - private static void OnIconChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - Button element = d as Button; - FrameworkElement oldElement = e.OldValue as FrameworkElement; - if (oldElement != null) element.RemoveLogicalChild(oldElement); - FrameworkElement newElement = e.NewValue as FrameworkElement; - if (newElement != null) element.AddLogicalChild(newElement); - } - #endregion - - #region LargeIcon - - /// - /// Gets or sets button large icon - /// - public object LargeIcon - { - get { return this.GetValue(LargeIconProperty); } - set { this.SetValue(LargeIconProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for SmallIcon. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty LargeIconProperty = - DependencyProperty.Register("LargeIcon", typeof(object), - typeof(Button), new FrameworkPropertyMetadata(null, null)); - - #endregion - - #region IsDefinitive - - /// - /// Gets or sets whether ribbon control click must close backstage - /// - public bool IsDefinitive - { - get { return (bool)this.GetValue(IsDefinitiveProperty); } - set { this.SetValue(IsDefinitiveProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for IsDefinitive. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IsDefinitiveProperty = - DependencyProperty.Register("IsDefinitive", typeof(bool), typeof(Button), new UIPropertyMetadata(true)); - - #endregion - - #region CornerRadius - - /// - /// Gets or sets the CornerRadius for the element - /// - public CornerRadius CornerRadius - { - get { return (CornerRadius)this.GetValue(CornerRadiusProperty); } - set { this.SetValue(CornerRadiusProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for CornerRadius. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty CornerRadiusProperty = - DependencyProperty.Register("CornerRadius", typeof(CornerRadius), typeof(Button), new PropertyMetadata(default(CornerRadius))); - - #endregion CornerRadius - - #endregion - - #region Constructors - - /// - /// Static constructor - /// - [SuppressMessage("Microsoft.Performance", "CA1810")] - static Button() - { - Type type = typeof(Button); - DefaultStyleKeyProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(type)); - ContextMenuService.Attach(type); - ToolTipService.Attach(type); - StyleProperty.OverrideMetadata(typeof(Button), new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceStyle))); - } - - // Coerce object style - static object OnCoerceStyle(DependencyObject d, object basevalue) - { - if (basevalue == null) - { - basevalue = (d as FrameworkElement).TryFindResource(typeof(Button)); - } - - return basevalue; - } - - /// - /// Default constructor - /// - public Button() - { - ContextMenuService.Coerce(this); - } - - #endregion - - #region Overrides - - /// - /// Called when a is clicked. - /// - protected override void OnClick() - { - // Close popup on click - if (this.IsDefinitive) - { - PopupService.RaiseDismissPopupEvent(this, DismissPopupMode.Always); - } - - base.OnClick(); - } - - #endregion - - #region Quick Access Item Creating - - /// - /// Gets control which represents shortcut item. - /// This item MUST be synchronized with the original - /// and send command to original one control. - /// - /// Control which represents shortcut item - public virtual FrameworkElement CreateQuickAccessItem() - { - Button button = new Button(); - button.Click += ((sender, e) => this.RaiseEvent(e)); - RibbonControl.BindQuickAccessItem(this, button); - return button; - } - - /// - /// Gets or sets whether control can be added to quick access toolbar - /// - public bool CanAddToQuickAccessToolBar - { - get { return (bool)this.GetValue(CanAddToQuickAccessToolBarProperty); } - set { this.SetValue(CanAddToQuickAccessToolBarProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for CanAddToQuickAccessToolBar. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty CanAddToQuickAccessToolBarProperty = RibbonControl.CanAddToQuickAccessToolBarProperty.AddOwner(typeof(Button), new UIPropertyMetadata(true, RibbonControl.OnCanAddToQuickAccessToolbarChanged)); - - #endregion - - #region Implementation of IKeyTipedControl - - /// - /// Handles key tip pressed - /// - public void OnKeyTipPressed() - { - this.OnClick(); - } - - /// - /// Handles back navigation with KeyTips - /// - public void OnKeyTipBack() - { - } - - #endregion - } +using System; +using System.Diagnostics.CodeAnalysis; +using System.Windows; +using System.Windows.Markup; + +namespace Fluent +{ + /// + /// Represents button + /// + [ContentProperty("Header")] + public class Button : System.Windows.Controls.Button, IRibbonControl, IQuickAccessItemProvider + { + #region Properties + + #region Size + + /// + /// Gets or sets Size for the element. + /// + public RibbonControlSize Size + { + get { return (RibbonControlSize)this.GetValue(SizeProperty); } + set { this.SetValue(SizeProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Size. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty SizeProperty = RibbonProperties.SizeProperty.AddOwner(typeof(Button)); + + #endregion + + #region SizeDefinition + + /// + /// Gets or sets SizeDefinition for element. + /// + public RibbonControlSizeDefinition SizeDefinition + { + get { return (RibbonControlSizeDefinition)this.GetValue(SizeDefinitionProperty); } + set { this.SetValue(SizeDefinitionProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for SizeDefinition. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty SizeDefinitionProperty = RibbonProperties.SizeDefinitionProperty.AddOwner(typeof(Button)); + + #endregion + + #region KeyTip + + /// + /// Gets or sets KeyTip for element. + /// + public string KeyTip + { + get { return (string)this.GetValue(KeyTipProperty); } + set { this.SetValue(KeyTipProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Keys. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty KeyTipProperty = Fluent.KeyTip.KeysProperty.AddOwner(typeof(Button)); + + #endregion + + #region Header + + /// + /// Gets or sets element Text + /// + public object Header + { + get { return (object)this.GetValue(HeaderProperty); } + set { this.SetValue(HeaderProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Header. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register("Header", typeof(object), typeof(Button), new PropertyMetadata(null)); + + #endregion + + #region Icon + + /// + /// Gets or sets Icon for the element + /// + public object Icon + { + get { return this.GetValue(IconProperty); } + set { this.SetValue(IconProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Icon. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IconProperty = RibbonControl.IconProperty.AddOwner(typeof(Button), new UIPropertyMetadata(null, OnIconChanged)); + + private static void OnIconChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + Button element = d as Button; + FrameworkElement oldElement = e.OldValue as FrameworkElement; + if (oldElement != null) element.RemoveLogicalChild(oldElement); + FrameworkElement newElement = e.NewValue as FrameworkElement; + if (newElement != null) element.AddLogicalChild(newElement); + } + #endregion + + #region LargeIcon + + /// + /// Gets or sets button large icon + /// + public object LargeIcon + { + get { return this.GetValue(LargeIconProperty); } + set { this.SetValue(LargeIconProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for SmallIcon. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty LargeIconProperty = + DependencyProperty.Register("LargeIcon", typeof(object), + typeof(Button), new FrameworkPropertyMetadata(null, null)); + + #endregion + + #region IsDefinitive + + /// + /// Gets or sets whether ribbon control click must close backstage + /// + public bool IsDefinitive + { + get { return (bool)this.GetValue(IsDefinitiveProperty); } + set { this.SetValue(IsDefinitiveProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for IsDefinitive. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IsDefinitiveProperty = + DependencyProperty.Register("IsDefinitive", typeof(bool), typeof(Button), new UIPropertyMetadata(true)); + + #endregion + + #region CornerRadius + + /// + /// Gets or sets the CornerRadius for the element + /// + public CornerRadius CornerRadius + { + get { return (CornerRadius)this.GetValue(CornerRadiusProperty); } + set { this.SetValue(CornerRadiusProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for CornerRadius. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty CornerRadiusProperty = + DependencyProperty.Register("CornerRadius", typeof(CornerRadius), typeof(Button), new PropertyMetadata(default(CornerRadius))); + + #endregion CornerRadius + + #endregion + + #region Constructors + + /// + /// Static constructor + /// + [SuppressMessage("Microsoft.Performance", "CA1810")] + static Button() + { + Type type = typeof(Button); + DefaultStyleKeyProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(type)); + ContextMenuService.Attach(type); + ToolTipService.Attach(type); + StyleProperty.OverrideMetadata(typeof(Button), new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceStyle))); + } + + // Coerce object style + static object OnCoerceStyle(DependencyObject d, object basevalue) + { + if (basevalue == null) + { + basevalue = (d as FrameworkElement).TryFindResource(typeof(Button)); + } + + return basevalue; + } + + /// + /// Default constructor + /// + public Button() + { + ContextMenuService.Coerce(this); + } + + #endregion + + #region Overrides + + /// + /// Called when a is clicked. + /// + protected override void OnClick() + { + // Close popup on click + if (this.IsDefinitive) + { + PopupService.RaiseDismissPopupEvent(this, DismissPopupMode.Always); + } + + base.OnClick(); + } + + #endregion + + #region Quick Access Item Creating + + /// + /// Gets control which represents shortcut item. + /// This item MUST be synchronized with the original + /// and send command to original one control. + /// + /// Control which represents shortcut item + public virtual FrameworkElement CreateQuickAccessItem() + { + Button button = new Button(); + button.Click += ((sender, e) => this.RaiseEvent(e)); + RibbonControl.BindQuickAccessItem(this, button); + return button; + } + + /// + /// Gets or sets whether control can be added to quick access toolbar + /// + public bool CanAddToQuickAccessToolBar + { + get { return (bool)this.GetValue(CanAddToQuickAccessToolBarProperty); } + set { this.SetValue(CanAddToQuickAccessToolBarProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for CanAddToQuickAccessToolBar. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty CanAddToQuickAccessToolBarProperty = RibbonControl.CanAddToQuickAccessToolBarProperty.AddOwner(typeof(Button), new UIPropertyMetadata(true, RibbonControl.OnCanAddToQuickAccessToolbarChanged)); + + #endregion + + #region Implementation of IKeyTipedControl + + /// + /// Handles key tip pressed + /// + public void OnKeyTipPressed() + { + this.OnClick(); + } + + /// + /// Handles back navigation with KeyTips + /// + public void OnKeyTipBack() + { + } + + #endregion + } } \ No newline at end of file diff --git a/Fluent/Controls/CheckBox.cs b/Fluent.Ribbon/Controls/CheckBox.cs similarity index 97% rename from Fluent/Controls/CheckBox.cs rename to Fluent.Ribbon/Controls/CheckBox.cs index 4f7ac0eec..093cd3537 100644 --- a/Fluent/Controls/CheckBox.cs +++ b/Fluent.Ribbon/Controls/CheckBox.cs @@ -1,234 +1,234 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.Windows; -using System.Windows.Data; -using System.Windows.Markup; -using System.Windows.Media; - -namespace Fluent -{ - /// - /// Represents Fluent UI specific CheckBox - /// - [ContentProperty("Header")] - public class CheckBox : System.Windows.Controls.CheckBox, IRibbonControl, IQuickAccessItemProvider - { - #region Properties - - #region Size - - /// - /// Gets or sets Size for the element. - /// - public RibbonControlSize Size - { - get { return (RibbonControlSize)this.GetValue(SizeProperty); } - set { this.SetValue(SizeProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Size. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty SizeProperty = RibbonProperties.SizeProperty.AddOwner(typeof(CheckBox)); - - #endregion - - #region SizeDefinition - - /// - /// Gets or sets SizeDefinition for element. - /// - public RibbonControlSizeDefinition SizeDefinition - { - get { return (RibbonControlSizeDefinition)this.GetValue(SizeDefinitionProperty); } - set { this.SetValue(SizeDefinitionProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for SizeDefinition. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty SizeDefinitionProperty = RibbonProperties.SizeDefinitionProperty.AddOwner(typeof(CheckBox)); - - #endregion - - #region KeyTip - - /// - /// Gets or sets KeyTip for element. - /// - public string KeyTip - { - get { return (string)this.GetValue(KeyTipProperty); } - set { this.SetValue(KeyTipProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Keys. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty KeyTipProperty = Fluent.KeyTip.KeysProperty.AddOwner(typeof(CheckBox)); - - #endregion - - #region Header - - /// - /// Gets or sets element Text - /// - public object Header - { - get { return (string)this.GetValue(HeaderProperty); } - set { this.SetValue(HeaderProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Header. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty HeaderProperty = RibbonControl.HeaderProperty.AddOwner(typeof(CheckBox)); - - #endregion - - #region Icon - - /// - /// Gets or sets Icon for the element - /// - public object Icon - { - get { return this.GetValue(IconProperty); } - set { this.SetValue(IconProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Icon. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IconProperty = RibbonControl.IconProperty.AddOwner(typeof(CheckBox), new UIPropertyMetadata(null, OnIconChanged)); - - private static void OnIconChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - CheckBox element = d as CheckBox; - FrameworkElement oldElement = e.OldValue as FrameworkElement; - if (oldElement != null) element.RemoveLogicalChild(oldElement); - FrameworkElement newElement = e.NewValue as FrameworkElement; - if (newElement != null) element.AddLogicalChild(newElement); - } - - #endregion - - #region LargeIcon - - /// - /// Gets or sets button large icon - /// - public ImageSource LargeIcon - { - get { return (ImageSource)this.GetValue(LargeIconProperty); } - set { this.SetValue(LargeIconProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for SmallIcon. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty LargeIconProperty = - DependencyProperty.Register("LargeIcon", typeof(ImageSource), - typeof(CheckBox), new UIPropertyMetadata(null)); - - #endregion - - #endregion - - #region Constructors - - /// - /// Static constructor - /// - [SuppressMessage("Microsoft.Performance", "CA1810")] - static CheckBox() - { - Type type = typeof(CheckBox); - DefaultStyleKeyProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(type)); - ContextMenuService.Attach(type); - ToolTipService.Attach(type); - StyleProperty.OverrideMetadata(typeof(CheckBox), new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceStyle))); - } - - // Coerce object style - static object OnCoerceStyle(DependencyObject d, object basevalue) - { - if (basevalue == null) - { - basevalue = (d as FrameworkElement).TryFindResource(typeof(CheckBox)); - } - - return basevalue; - } - - /// - /// Default constructor - /// - public CheckBox() - { - ContextMenuService.Coerce(this); - } - - #endregion - - #region Quick Access Item Creating - - /// - /// Gets control which represents shortcut item. - /// This item MUST be syncronized with the original - /// and send command to original one control. - /// - /// Control which represents shortcut item - public virtual FrameworkElement CreateQuickAccessItem() - { - CheckBox button = new CheckBox(); - - RibbonControl.Bind(this, button, "IsChecked", IsCheckedProperty, BindingMode.TwoWay); - button.Click += ((sender, e) => this.RaiseEvent(e)); - RibbonControl.BindQuickAccessItem(this, button); - - return button; - } - - /// - /// Gets or sets whether control can be added to quick access toolbar - /// - public bool CanAddToQuickAccessToolBar - { - get { return (bool)this.GetValue(CanAddToQuickAccessToolBarProperty); } - set { this.SetValue(CanAddToQuickAccessToolBarProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for CanAddToQuickAccessToolBar. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty CanAddToQuickAccessToolBarProperty = RibbonControl.CanAddToQuickAccessToolBarProperty.AddOwner(typeof(CheckBox), new UIPropertyMetadata(true, RibbonControl.OnCanAddToQuickAccessToolbarChanged)); - - #endregion - - #region Implementation of IKeyTipedControl - - /// - /// Handles key tip pressed - /// - public void OnKeyTipPressed() - { - this.OnClick(); - } - - /// - /// Handles back navigation with KeyTips - /// - public void OnKeyTipBack() - { - } - - #endregion - } +using System; +using System.Diagnostics.CodeAnalysis; +using System.Windows; +using System.Windows.Data; +using System.Windows.Markup; +using System.Windows.Media; + +namespace Fluent +{ + /// + /// Represents Fluent UI specific CheckBox + /// + [ContentProperty("Header")] + public class CheckBox : System.Windows.Controls.CheckBox, IRibbonControl, IQuickAccessItemProvider + { + #region Properties + + #region Size + + /// + /// Gets or sets Size for the element. + /// + public RibbonControlSize Size + { + get { return (RibbonControlSize)this.GetValue(SizeProperty); } + set { this.SetValue(SizeProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Size. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty SizeProperty = RibbonProperties.SizeProperty.AddOwner(typeof(CheckBox)); + + #endregion + + #region SizeDefinition + + /// + /// Gets or sets SizeDefinition for element. + /// + public RibbonControlSizeDefinition SizeDefinition + { + get { return (RibbonControlSizeDefinition)this.GetValue(SizeDefinitionProperty); } + set { this.SetValue(SizeDefinitionProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for SizeDefinition. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty SizeDefinitionProperty = RibbonProperties.SizeDefinitionProperty.AddOwner(typeof(CheckBox)); + + #endregion + + #region KeyTip + + /// + /// Gets or sets KeyTip for element. + /// + public string KeyTip + { + get { return (string)this.GetValue(KeyTipProperty); } + set { this.SetValue(KeyTipProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Keys. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty KeyTipProperty = Fluent.KeyTip.KeysProperty.AddOwner(typeof(CheckBox)); + + #endregion + + #region Header + + /// + /// Gets or sets element Text + /// + public object Header + { + get { return (string)this.GetValue(HeaderProperty); } + set { this.SetValue(HeaderProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Header. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty HeaderProperty = RibbonControl.HeaderProperty.AddOwner(typeof(CheckBox)); + + #endregion + + #region Icon + + /// + /// Gets or sets Icon for the element + /// + public object Icon + { + get { return this.GetValue(IconProperty); } + set { this.SetValue(IconProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Icon. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IconProperty = RibbonControl.IconProperty.AddOwner(typeof(CheckBox), new UIPropertyMetadata(null, OnIconChanged)); + + private static void OnIconChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + CheckBox element = d as CheckBox; + FrameworkElement oldElement = e.OldValue as FrameworkElement; + if (oldElement != null) element.RemoveLogicalChild(oldElement); + FrameworkElement newElement = e.NewValue as FrameworkElement; + if (newElement != null) element.AddLogicalChild(newElement); + } + + #endregion + + #region LargeIcon + + /// + /// Gets or sets button large icon + /// + public ImageSource LargeIcon + { + get { return (ImageSource)this.GetValue(LargeIconProperty); } + set { this.SetValue(LargeIconProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for SmallIcon. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty LargeIconProperty = + DependencyProperty.Register("LargeIcon", typeof(ImageSource), + typeof(CheckBox), new UIPropertyMetadata(null)); + + #endregion + + #endregion + + #region Constructors + + /// + /// Static constructor + /// + [SuppressMessage("Microsoft.Performance", "CA1810")] + static CheckBox() + { + Type type = typeof(CheckBox); + DefaultStyleKeyProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(type)); + ContextMenuService.Attach(type); + ToolTipService.Attach(type); + StyleProperty.OverrideMetadata(typeof(CheckBox), new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceStyle))); + } + + // Coerce object style + static object OnCoerceStyle(DependencyObject d, object basevalue) + { + if (basevalue == null) + { + basevalue = (d as FrameworkElement).TryFindResource(typeof(CheckBox)); + } + + return basevalue; + } + + /// + /// Default constructor + /// + public CheckBox() + { + ContextMenuService.Coerce(this); + } + + #endregion + + #region Quick Access Item Creating + + /// + /// Gets control which represents shortcut item. + /// This item MUST be syncronized with the original + /// and send command to original one control. + /// + /// Control which represents shortcut item + public virtual FrameworkElement CreateQuickAccessItem() + { + CheckBox button = new CheckBox(); + + RibbonControl.Bind(this, button, "IsChecked", IsCheckedProperty, BindingMode.TwoWay); + button.Click += ((sender, e) => this.RaiseEvent(e)); + RibbonControl.BindQuickAccessItem(this, button); + + return button; + } + + /// + /// Gets or sets whether control can be added to quick access toolbar + /// + public bool CanAddToQuickAccessToolBar + { + get { return (bool)this.GetValue(CanAddToQuickAccessToolBarProperty); } + set { this.SetValue(CanAddToQuickAccessToolBarProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for CanAddToQuickAccessToolBar. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty CanAddToQuickAccessToolBarProperty = RibbonControl.CanAddToQuickAccessToolBarProperty.AddOwner(typeof(CheckBox), new UIPropertyMetadata(true, RibbonControl.OnCanAddToQuickAccessToolbarChanged)); + + #endregion + + #region Implementation of IKeyTipedControl + + /// + /// Handles key tip pressed + /// + public void OnKeyTipPressed() + { + this.OnClick(); + } + + /// + /// Handles back navigation with KeyTips + /// + public void OnKeyTipBack() + { + } + + #endregion + } } \ No newline at end of file diff --git a/Fluent/Controls/ColorGallery.cs b/Fluent.Ribbon/Controls/ColorGallery.cs similarity index 97% rename from Fluent/Controls/ColorGallery.cs rename to Fluent.Ribbon/Controls/ColorGallery.cs index de4743448..cb077349a 100644 --- a/Fluent/Controls/ColorGallery.cs +++ b/Fluent.Ribbon/Controls/ColorGallery.cs @@ -1,1074 +1,1074 @@ -namespace Fluent -{ - using System; - using System.Collections; - using System.Collections.Generic; - using System.Collections.ObjectModel; - using System.Collections.Specialized; - using System.Diagnostics; - using System.Runtime.InteropServices; - using System.Windows; - using System.Windows.Controls; - using System.Windows.Interop; - using System.Windows.Markup; - using System.Windows.Media; - using System.Windows.Threading; - - /// - /// Represents color gallery modes - /// - public enum ColorGalleryMode - { - /// - /// Color gallery displays only fixed highlight colors - /// - HighlightColors = 0, - - /// - /// Color gallery displays only fixed standart colors - /// - StandardColors, - - /// - /// Color gallery displays theme colors - /// - ThemeColors - } - - /// - /// Date template selector for gradients - /// - public class ColorGradientItemTemplateSelector : DataTemplateSelector - { - /// - /// When overridden in a derived class, returns a based on custom logic. - /// - /// - /// Returns a or null. The default value is null. - /// - /// The data object for which to select the template.The data-bound object. - public override DataTemplate SelectTemplate(object item, DependencyObject container) - { - if (item == null) - { - return null; - } - - ListBox listBox = null; - var parent = container; - while (parent != null) - { - parent = VisualTreeHelper.GetParent(parent); - listBox = parent as ListBox; - if (listBox != null) - { - break; - } - } - - if (listBox == null) - { - return null; - } - - ColorGallery colorGallery = null; - while (parent != null) - { - parent = VisualTreeHelper.GetParent(parent); - colorGallery = parent as ColorGallery; - if (colorGallery != null) break; - } - - if (colorGallery == null) - { - return null; - } - - var index = listBox.Items.IndexOf(item); - if (index < colorGallery.Columns) - { - return listBox.TryFindResource("GradientColorTopDataTemplate") as DataTemplate; - } - - if (index >= listBox.Items.Count - colorGallery.Columns) - { - return listBox.TryFindResource("GradientColorBottomDataTemplate") as DataTemplate; - } - - return listBox.TryFindResource("GradientColorCenterDataTemplate") as DataTemplate; - } - - } - - /// - /// More colors event args - /// - public class MoreColorsExecutingEventArgs : EventArgs - { - /// - /// Gets or sets choosed color - /// - public Color Color { get; set; } - /// - /// Gets or sets a value indicating whether more colors is canceled - /// - public bool Canceled { get; set; } - } - - /// - /// Represents color gallery - /// - [ContentProperty("ThemeColors")] - public class ColorGallery : Control - { - #region Constants - - /// - /// Hightlight colors array - /// - public static readonly Color[] HighlightColors = new Color[] - { - Color.FromRgb(0xFF ,0xFF ,0x00), - Color.FromRgb(0x00 ,0xFF ,0x00), - Color.FromRgb(0x00 ,0xFF ,0xFF), - - Color.FromRgb(0xFF ,0x00 ,0xFF), - Color.FromRgb(0x00 ,0x00 ,0xFF), - Color.FromRgb(0xFF ,0x00 ,0x00), - - Color.FromRgb(0x00 ,0x00 ,0x80), - Color.FromRgb(0x00 ,0x80 ,0x80), - Color.FromRgb(0x00 ,0x80 ,0x00), - - Color.FromRgb(0x80 ,0x00 ,0x80), - Color.FromRgb(0x80 ,0x00 ,0x00), - Color.FromRgb(0x80 ,0x80 ,0x00), - - Color.FromRgb(0x80 ,0x80 ,0x80), - Color.FromRgb(0xC0 ,0xC0 ,0xC0), - Color.FromRgb(0x00 ,0x00 ,0x00), - }; - - /// - /// Standard colors array - /// - public static readonly Color[] StandardColors = new Color[] - { - Color.FromRgb(0xFF ,0xFF ,0xFF), - Color.FromRgb(0xFF ,0x00 ,0x00), - Color.FromRgb(0xC0 ,0x50 ,0x4D), - Color.FromRgb(0xD1 ,0x63 ,0x49), - Color.FromRgb(0xDD ,0x84 ,0x84), - - Color.FromRgb(0xCC ,0xCC ,0xCC), - Color.FromRgb(0xFF ,0xC0 ,0x00), - Color.FromRgb(0xF7 ,0x96 ,0x46), - Color.FromRgb(0xD1 ,0x90 ,0x49), - Color.FromRgb(0xF3 ,0xA4 ,0x47), - - Color.FromRgb(0xA5 ,0xA5 ,0xA5), - Color.FromRgb(0xFF ,0xFF ,0x00), - Color.FromRgb(0x9B ,0xBB ,0x59), - Color.FromRgb(0xCC ,0xB4 ,0x00), - Color.FromRgb(0xDF ,0xCE ,0x04), - - Color.FromRgb(0x66 ,0x66 ,0x66), - Color.FromRgb(0x00 ,0xB0 ,0x50), - Color.FromRgb(0x4B ,0xAC ,0xC6), - Color.FromRgb(0x8F ,0xB0 ,0x8C), - Color.FromRgb(0xA5 ,0xB5 ,0x92), - - Color.FromRgb(0x33 ,0x33 ,0x33), - Color.FromRgb(0x00 ,0x4D ,0xBB), - Color.FromRgb(0x4F ,0x81 ,0xBD), - Color.FromRgb(0x64 ,0x6B ,0x86), - Color.FromRgb(0x80 ,0x9E ,0xC2), - - Color.FromRgb(0x00 ,0x00 ,0x00), - Color.FromRgb(0x9B ,0x00 ,0xD3), - Color.FromRgb(0x80 ,0x64 ,0xA2), - Color.FromRgb(0x9E ,0x7C ,0x7C), - Color.FromRgb(0x9C ,0x85 ,0xC0), - }; - - /// - /// Standard colors array in ThemeColor mode - /// - public static readonly Color[] StandardThemeColors = new Color[] - { - Color.FromRgb(0xC0 ,0x00 ,0x00), - Color.FromRgb(0xFF ,0x00 ,0x00), - Color.FromRgb(0xFF ,0xC0 ,0x00), - Color.FromRgb(0xFF ,0xFF ,0x00), - Color.FromRgb(0x92 ,0xD0 ,0x50), - Color.FromRgb(0x00 ,0xB0 ,0x50), - Color.FromRgb(0x00 ,0xB0 ,0xF0), - Color.FromRgb(0x00 ,0x70 ,0xC0), - Color.FromRgb(0x00 ,0x20 ,0x60), - Color.FromRgb(0x70 ,0x30 ,0xA0), - }; - - #endregion - - #region RecentItems - - private static ObservableCollection recentColors; - - /// - /// Gets recent colors collection - /// - public static ObservableCollection RecentColors - { - get - { - if (recentColors == null) recentColors = new ObservableCollection(); - return recentColors; - } - } - - #endregion - - #region Fields - - private MenuItem noColorButton; - private MenuItem automaticButton; - - private MenuItem moreColorsButton; - - private ListBox themeColorsListBox; - private ListBox themeGradientsListBox; - private ListBox standardColorsListBox; - private ListBox standardGradientsListBox; - private ListBox recentColorsListBox; - - private readonly List listBoxes = new List(); - - private bool isSelectionChanging; - - private bool isTemplateApplied; - - - #endregion - - #region Properties - - #region Mode - - /// - /// Gets or sets color gallery mode - /// - public ColorGalleryMode Mode - { - get { return (ColorGalleryMode)this.GetValue(ModeProperty); } - set { this.SetValue(ModeProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Mode. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty ModeProperty = - DependencyProperty.Register("Mode", typeof(ColorGalleryMode), typeof(ColorGallery), new UIPropertyMetadata(ColorGalleryMode.StandardColors, OnModeChanged)); - - private static void OnModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - (d as ColorGallery).UpdateGradients(); - } - - #endregion - - #region Chip Size - - /// - /// Gets or sets chip width - /// - public double ChipWidth - { - get { return (double)this.GetValue(ChipWidthProperty); } - set { this.SetValue(ChipWidthProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for ChipWidth. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty ChipWidthProperty = - DependencyProperty.Register("ChipWidth", typeof(double), typeof(ColorGallery), new UIPropertyMetadata(13.0, null, CoerceChipSize)); - - private static object CoerceChipSize(DependencyObject d, object basevalue) - { - double value = (double)basevalue; - if (value < 0) return 0; - return basevalue; - } - - /// - /// Gets or sets chip height - /// - public double ChipHeight - { - get { return (double)this.GetValue(ChipHeightProperty); } - set { this.SetValue(ChipHeightProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for ChipHeight. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty ChipHeightProperty = - DependencyProperty.Register("ChipHeight", typeof(double), typeof(ColorGallery), new UIPropertyMetadata(13.0, null, CoerceChipSize)); - - #endregion - - #region IsAutomaticColorButtonVisible - - /// - /// Gets or sets a value indicating whether Automatic button is visible - /// - public bool IsAutomaticColorButtonVisible - { - get { return (bool)this.GetValue(IsAutomaticColorButtonVisibleProperty); } - set { this.SetValue(IsAutomaticColorButtonVisibleProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for IsAutomaticColorButtonVisible. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IsAutomaticColorButtonVisibleProperty = - DependencyProperty.Register("IsAutomaticColorButtonVisible", typeof(bool), typeof(ColorGallery), new UIPropertyMetadata(true)); - - #endregion - - #region IsNoColorButtonVisible - - /// - /// Gets or sets a value indicating whether No color button is visible - /// - public bool IsNoColorButtonVisible - { - get { return (bool)this.GetValue(IsNoColorButtonVisibleProperty); } - set { this.SetValue(IsNoColorButtonVisibleProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for IsNoColorButtonVisible. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IsNoColorButtonVisibleProperty = - DependencyProperty.Register("IsNoColorButtonVisible", typeof(bool), typeof(ColorGallery), new UIPropertyMetadata(true)); - - #endregion - - #region IsMoreColorsButtonVisible - - /// - /// Gets or sets a value indicating whether More Colors button is visible - /// - public bool IsMoreColorsButtonVisible - { - get { return (bool)this.GetValue(IsMoreColorsButtonVisibleProperty); } - set { this.SetValue(IsMoreColorsButtonVisibleProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for IsMoreColorsButtonVisible. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IsMoreColorsButtonVisibleProperty = - DependencyProperty.Register("IsMoreColorsButtonVisible", typeof(bool), typeof(ColorGallery), new UIPropertyMetadata(true)); - - #endregion - - #region IsRecentColorsVisible - - /// - /// Gets or set a value indicating whether recent colors group displayed. Work only when Mode is ThemeColors - /// - public bool IsRecentColorsVisible - { - get { return (bool)this.GetValue(IsRecentColorsVisibleProperty); } - set { this.SetValue(IsRecentColorsVisibleProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for IsRecentColorsVisible. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IsRecentColorsVisibleProperty = - DependencyProperty.Register("IsRecentColorsVisible", typeof(bool), typeof(ColorGallery), new UIPropertyMetadata(true)); - - - - #endregion - - #region Columns - - /// - /// Gets or sets number of color gallery columns. It works only when Mode is ThemeColors - /// - public int Columns - { - get { return (int)this.GetValue(ColumnsProperty); } - set { this.SetValue(ColumnsProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Columns. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty ColumnsProperty = - DependencyProperty.Register("Columns", typeof(int), typeof(ColorGallery), new UIPropertyMetadata(10, OnColumnsChanged, CoerceColumns)); - - private static object CoerceColumns(DependencyObject d, object basevalue) - { - int value = (int)basevalue; - if (value < 1) return 1; - return basevalue; - } - - private static void OnColumnsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - (d as ColorGallery).UpdateGradients(); - } - - #endregion - - #region StandardColorGridRows - - /// - /// Gets or set number of standard color rows. Work only when Mode is ThemeColors - /// - public int StandardColorGridRows - { - get { return (int)this.GetValue(StandardColorGridRowsProperty); } - set { this.SetValue(StandardColorGridRowsProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for StandardColorGridRows. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty StandardColorGridRowsProperty = - DependencyProperty.Register("StandardColorGridRows", typeof(int), typeof(ColorGallery), new UIPropertyMetadata(0, OnStandardColorGridRowsChanged, CoeceGridRows)); - - private static object CoeceGridRows(DependencyObject d, object basevalue) - { - int value = (int)basevalue; - if (value < 0) return 0; - return basevalue; - } - - private static void OnStandardColorGridRowsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - (d as ColorGallery).UpdateGradients(); - } - - #endregion - - #region ThemeColorGridRows - - /// - /// Gets or set number of theme color rows. Work only when Mode is ThemeColors - /// - public int ThemeColorGridRows - { - get { return (int)this.GetValue(ThemeColorGridRowsProperty); } - set { this.SetValue(ThemeColorGridRowsProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for ThemeColorGridRows. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty ThemeColorGridRowsProperty = - DependencyProperty.Register("ThemeColorGridRows", typeof(int), typeof(ColorGallery), new UIPropertyMetadata(0, OnThemeColorGridRowsChanged, CoeceGridRows)); - - private static void OnThemeColorGridRowsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - (d as ColorGallery).UpdateGradients(); - } - - #endregion - - #region SelectedColor - - /// - /// Gets or sets selected color - /// - public Color? SelectedColor - { - get { return (Color?)this.GetValue(SelectedColorProperty); } - set { this.SetValue(SelectedColorProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for SelectedColor. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty SelectedColorProperty = - DependencyProperty.Register("SelectedColor", typeof(Color?), typeof(ColorGallery), new UIPropertyMetadata(null, OnSelectedColorChanged)); - - // Handles selected color changed - private static void OnSelectedColorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var gallery = d as ColorGallery; - - if (gallery == null) - { - return; - } - - // Raise event - gallery.RaiseEvent(new RoutedEventArgs(SelectedColorChangedEvent)); - - // Set color in gallery - var color = (Color?)e.NewValue; - gallery.UpdateSelectedColor(color); - } - - private void UpdateSelectedColor(Color? color) - { - if (this.isSelectionChanging - || this.isTemplateApplied == false) - { - return; - } - - this.isSelectionChanging = true; - var isSetted = false; - - // Check menu items - if (color.HasValue == false) - { - isSetted = true; - this.automaticButton.IsChecked = true; - this.noColorButton.IsChecked = false; - } - else if (color.Value == Colors.Transparent) - { - isSetted = true; - this.automaticButton.IsChecked = false; - this.noColorButton.IsChecked = true; - } - else - { - this.automaticButton.IsChecked = false; - this.noColorButton.IsChecked = false; - } - - // Remove selection from others - for (var i = 0; i < this.listBoxes.Count; i++) - { - if (isSetted == false - && this.listBoxes[i].Visibility == Visibility.Visible) - { - if (this.listBoxes[i].Items.Contains(color.Value)) - { - this.listBoxes[i].SelectedItem = color.Value; - isSetted = true; - } - } - else - { - this.listBoxes[i].SelectedItem = null; - } - } - - this.isSelectionChanging = false; - } - - #endregion - - #region ThemeColors - - private ObservableCollection themeColors; - - /// - /// Gets collection of theme colors - /// - public ObservableCollection ThemeColors - { - get - { - if (this.themeColors == null) - { - this.themeColors = new ObservableCollection(); - this.themeColors.CollectionChanged += this.OnThemeColorsChanged; - } - return this.themeColors; - } - } - - private void OnThemeColorsChanged(object sender, NotifyCollectionChangedEventArgs e) - { - this.UpdateGradients(); - } - - /// - /// Gets or sets theme colors source - /// - public IEnumerable ThemeColorsSource - { - get { return (IEnumerable)this.GetValue(ThemeColorsSourceProperty); } - set { this.SetValue(ThemeColorsSourceProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for ThemeColorsSource. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty ThemeColorsSourceProperty = - DependencyProperty.Register("ThemeColorsSource", typeof(IEnumerable), typeof(ColorGallery), new UIPropertyMetadata(null, OnThemeColorsSourceChanged)); - - private static void OnThemeColorsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - ColorGallery gal = d as ColorGallery; - gal.ThemeColors.Clear(); - if (e.NewValue != null) - { - foreach (Color color in (e.NewValue as IEnumerable)) - { - gal.ThemeColors.Add(color); - } - } - } - - #endregion - - #region ThemeGradients - - /// - /// Gets theme gradients collection - /// - public Color[] ThemeGradients - { - get { return (Color[])this.GetValue(ThemeGradientsProperty); } - private set { this.SetValue(ThemeGradientsPropertyKey, value); } - } - - // - private static readonly DependencyPropertyKey ThemeGradientsPropertyKey = - DependencyProperty.RegisterReadOnly("ThemeGradients", typeof(Color[]), typeof(ColorGallery), new UIPropertyMetadata(null)); - - /// - /// Using a DependencyProperty as the backing store for ThemeGradients. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty ThemeGradientsProperty = ThemeGradientsPropertyKey.DependencyProperty; - - #endregion - - #region StandardGradients - - /// - /// Gets standart gradients collection - /// - public Color[] StandardGradients - { - get { return (Color[])this.GetValue(StandardGradientsProperty); } - private set { this.SetValue(StandardGradientsPropertyKey, value); } - } - - - private static readonly DependencyPropertyKey StandardGradientsPropertyKey = - DependencyProperty.RegisterReadOnly("StandardGradients", typeof(Color[]), typeof(ColorGallery), new UIPropertyMetadata(null)); - - /// - /// Using a DependencyProperty as the backing store for ThemeGradients. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty StandardGradientsProperty = StandardGradientsPropertyKey.DependencyProperty; - - #endregion - - #endregion - - #region Events - - /// - /// Occurs when selection color is changed - /// - public event RoutedEventHandler SelectedColorChanged - { - add - { - this.AddHandler(SelectedColorChangedEvent, value); - } - remove - { - this.RemoveHandler(SelectedColorChangedEvent, value); - } - } - /// - /// Identifies the SelectedColorChanged routed event. - /// - public static readonly RoutedEvent SelectedColorChangedEvent = EventManager.RegisterRoutedEvent("SelectedColorChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(ColorGallery)); - - /// - /// Raises SelectedColorChanged event - /// - public void RaiseSelectedColorChanged() - { - this.RaiseEvent(new RoutedEventArgs(SelectedColorChangedEvent, this)); - } - - /// - /// Occurs whether more colors menu item is clicked - /// - public event EventHandler MoreColorsExecuting; - - #endregion - - #region Initializing - - /// - /// Static constructor - /// - static ColorGallery() - { - Type type = typeof(ColorGallery); - DefaultStyleKeyProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(type)); - ContextMenuService.Attach(type); - StyleProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceStyle))); - } - - // Coerce object style - private static object OnCoerceStyle(DependencyObject d, object basevalue) - { - if (basevalue == null) - { - basevalue = (d as FrameworkElement).TryFindResource(typeof(ColorGallery)); - } - - return basevalue; - } - - /// - /// Default constructor - /// - public ColorGallery() - { - } - - #endregion - - #region Overrides - - /// - /// When overridden in a derived class, is invoked whenever application code or internal processes call . - /// - public override void OnApplyTemplate() - { - base.OnApplyTemplate(); - - if (this.moreColorsButton != null) - this.moreColorsButton.Click += this.OnMoreColorsClick; - this.moreColorsButton = this.GetTemplateChild("PART_MoreColors") as MenuItem; - if (this.moreColorsButton != null) - this.moreColorsButton.Click += this.OnMoreColorsClick; - - if (this.noColorButton != null) - this.noColorButton.Click -= this.OnNoColorClick; - this.noColorButton = this.GetTemplateChild("PART_NoColor") as MenuItem; - if (this.noColorButton != null) - this.noColorButton.Click += this.OnNoColorClick; - - if (this.automaticButton != null) - this.automaticButton.Click -= this.OnAutomaticClick; - this.automaticButton = this.GetTemplateChild("PART_AutomaticColor") as MenuItem; - if (this.automaticButton != null) - this.automaticButton.Click += this.OnAutomaticClick; - - // List boxes - this.listBoxes.Clear(); - - if (this.themeColorsListBox != null) - this.themeColorsListBox.SelectionChanged -= this.OnListBoxSelectedChanged; - this.themeColorsListBox = this.GetTemplateChild("PART_ThemeColorsListBox") as ListBox; - this.listBoxes.Add(this.themeColorsListBox); - if (this.themeColorsListBox != null) - this.themeColorsListBox.SelectionChanged += this.OnListBoxSelectedChanged; - - if (this.themeGradientsListBox != null) - this.themeGradientsListBox.SelectionChanged -= this.OnListBoxSelectedChanged; - this.themeGradientsListBox = this.GetTemplateChild("PART_ThemeGradientColorsListBox") as ListBox; - this.listBoxes.Add(this.themeGradientsListBox); - if (this.themeGradientsListBox != null) - this.themeGradientsListBox.SelectionChanged += this.OnListBoxSelectedChanged; - - if (this.standardColorsListBox != null) - this.standardColorsListBox.SelectionChanged -= this.OnListBoxSelectedChanged; - this.standardColorsListBox = this.GetTemplateChild("PART_StandardColorsListBox") as ListBox; - this.listBoxes.Add(this.standardColorsListBox); - if (this.standardColorsListBox != null) - this.standardColorsListBox.SelectionChanged += this.OnListBoxSelectedChanged; - - if (this.standardGradientsListBox != null) - this.standardGradientsListBox.SelectionChanged -= this.OnListBoxSelectedChanged; - this.standardGradientsListBox = this.GetTemplateChild("PART_StandardGradientColorsListBox") as ListBox; - this.listBoxes.Add(this.standardGradientsListBox); - if (this.standardGradientsListBox != null) - this.standardGradientsListBox.SelectionChanged += this.OnListBoxSelectedChanged; - - if (this.recentColorsListBox != null) - this.recentColorsListBox.SelectionChanged -= this.OnListBoxSelectedChanged; - this.recentColorsListBox = this.GetTemplateChild("PART_RecentColorsListBox") as ListBox; - this.listBoxes.Add(this.recentColorsListBox); - if (this.recentColorsListBox != null) - this.recentColorsListBox.SelectionChanged += this.OnListBoxSelectedChanged; - - this.isTemplateApplied = true; - - this.UpdateSelectedColor(this.SelectedColor); - } - - #endregion - - #region Private Methods - - private static IntPtr customColors = IntPtr.Zero; - private readonly int[] colorsArray = new int[16]; - - private void OnMoreColorsClick(object sender, RoutedEventArgs e) - { - if (this.MoreColorsExecuting != null) - { - MoreColorsExecutingEventArgs args = new MoreColorsExecutingEventArgs(); - this.MoreColorsExecuting(this, args); - if (!args.Canceled) - { - Color color = args.Color; - if (RecentColors.Contains(color)) RecentColors.Remove(color); - RecentColors.Insert(0, color); - this.recentColorsListBox.SelectedIndex = 0; - } - } - else - { - NativeMethods.CHOOSECOLOR chooseColor = new NativeMethods.CHOOSECOLOR(); - Window wnd = Window.GetWindow(this); - if (wnd != null) chooseColor.hwndOwner = new WindowInteropHelper(wnd).Handle; - chooseColor.Flags = NativeMethods.CC_ANYCOLOR; - if (customColors == IntPtr.Zero) - { - // Set custom colors) - for (int i = 0; i < this.colorsArray.Length; i++) - this.colorsArray[i] = 0x00FFFFFF; - customColors = GCHandle.Alloc(this.colorsArray, GCHandleType.Pinned).AddrOfPinnedObject(); - } - chooseColor.lpCustColors = customColors; - if (NativeMethods.ChooseColor(chooseColor)) - { - Color color = ConvertFromWin32Color(chooseColor.rgbResult); - if (RecentColors.Contains(color)) RecentColors.Remove(color); - RecentColors.Insert(0, color); - this.recentColorsListBox.SelectedIndex = 0; - } - } - } - - private static Color ConvertFromWin32Color(int color) - { - int r = color & 0x000000FF; - int g = (color & 0x0000FF00) >> 8; - int b = (color & 0x00FF0000) >> 16; - return Color.FromArgb(255, (byte)r, (byte)g, (byte)b); - } - - private void OnAutomaticClick(object sender, RoutedEventArgs e) - { - this.isSelectionChanging = true; - this.noColorButton.IsChecked = false; - this.automaticButton.IsChecked = true; - // Remove selection from listboxes - for (int i = 0; i < this.listBoxes.Count; i++) - { - this.listBoxes[i].SelectedItem = null; - } - - this.SelectedColor = null; - this.isSelectionChanging = false; - } - - private void OnNoColorClick(object sender, RoutedEventArgs e) - { - this.isSelectionChanging = true; - this.noColorButton.IsChecked = true; - this.automaticButton.IsChecked = false; - // Remove selection from listboxes - for (int i = 0; i < this.listBoxes.Count; i++) - { - this.listBoxes[i].SelectedItem = null; - } - - this.SelectedColor = Colors.Transparent; - this.isSelectionChanging = false; - } - - private void OnListBoxSelectedChanged(object sender, SelectionChangedEventArgs e) - { - if (this.isSelectionChanging) return; - this.isSelectionChanging = true; - if (e.AddedItems != null && e.AddedItems.Count > 0) - { - // Remove selection from others - this.noColorButton.IsChecked = false; - this.automaticButton.IsChecked = false; - for (int i = 0; i < this.listBoxes.Count; i++) - { - if (this.listBoxes[i] != sender) - this.listBoxes[i].SelectedItem = null; - } - this.SelectedColor = (Color)e.AddedItems[0]; - PopupService.RaiseDismissPopupEvent(this, DismissPopupMode.Always); - } - this.isSelectionChanging = false; - } - - private void UpdateGradients() - { - if ((this.Mode == ColorGalleryMode.ThemeColors) && (this.Columns > 0)) - { - if (this.ThemeColorGridRows > 0) - { - this.ThemeGradients = this.GenerateThemeGradients(); - } - else - this.ThemeGradients = null; - - if (this.StandardColorGridRows > 0) - { - this.StandardGradients = this.GenerateStandardGradients(); - } - else - this.StandardGradients = null; - } - else - { - this.StandardGradients = null; - this.ThemeGradients = null; - } - } - - private Color[] GenerateStandardGradients() - { - int count = Math.Min(this.Columns, StandardThemeColors.Length); - Color[] result = new Color[this.Columns *this.StandardColorGridRows]; - for (int i = 0; i < count; i++) - { - Color[] colors = GetGradient(StandardThemeColors[i], this.StandardColorGridRows); - for (int j = 0; j < this.StandardColorGridRows; j++) - { - result[i + j *this.Columns] = colors[j]; - } - } - return result; - } - - private Color[] GenerateThemeGradients() - { - int count = Math.Min(this.Columns, this.ThemeColors.Count); - Color[] result = new Color[this.Columns *this.ThemeColorGridRows]; - for (int i = 0; i < count; i++) - { - Color[] colors = GetGradient(this.ThemeColors[i], this.ThemeColorGridRows); - for (int j = 0; j < this.ThemeColorGridRows; j++) - { - result[i + j *this.Columns] = colors[j]; - } - } - return result; - } - - #endregion - - #region Gradient Generation - - /// - /// Returns brightness of the given color from 0..1 - /// - /// Color - /// Brightness of the given color from 0..1 - static double GetBrightness(Color color) - { - double summ = ((double)color.R) + ((double)color.G) + ((double)color.B); - return summ / (255.0 * 3.0); - } - - // Makes the given color lighter - static Color Lighter(Color color, double power) - { - double totalAvailability = 255.0 * 3.0 - (double)color.R + (double)color.G + (double)color.B; - double redAvailability; - double greenAvailability; - double blueAvailability; - double needToBeAdded; - - if (color.R + color.G + color.B == 0) - { - redAvailability = 1.0 / 3.0; - greenAvailability = 1.0 / 3.0; - blueAvailability = 1.0 / 3.0; - needToBeAdded = power * 255.0 * 3.0; - } - else - { - redAvailability = (255.0 - (double)color.R) / totalAvailability; - greenAvailability = (255.0 - (double)color.G) / totalAvailability; - blueAvailability = (255.0 - (double)color.B) / totalAvailability; - needToBeAdded = ((double)color.R + (double)color.G + (double)color.B) * (power - 1); - } - - - Color result = Color.FromRgb( - (byte)(color.R + ((byte)(redAvailability * needToBeAdded))), - (byte)(color.G + ((byte)(greenAvailability * needToBeAdded))), - (byte)(color.B + ((byte)(blueAvailability * needToBeAdded)))); - - return result; - } - - // Makes the given color darker - static Color Darker(Color color, double power) - { - double totalAvailability = (double)color.R + (double)color.G + (double)color.B; - double redAvailability = ((double)color.R) / totalAvailability; - double greenAvailability = ((double)color.G) / totalAvailability; - double blueAvailability = ((double)color.B) / totalAvailability; - - double needToBeAdded = ((double)color.R + (double)color.G + (double)color.B); - needToBeAdded = needToBeAdded - needToBeAdded * power; - - Color result = Color.FromRgb( - (byte)(color.R - ((byte)(redAvailability * needToBeAdded))), - (byte)(color.G - ((byte)(greenAvailability * needToBeAdded))), - (byte)(color.B - ((byte)(blueAvailability * needToBeAdded)))); - - return result; - } - - // Makes a new color from the given with new brightness - static Color Rebright(Color color, double newBrightness) - { - double currentBrightness = GetBrightness(color); - double power = currentBrightness != 0.0 ? newBrightness / currentBrightness : (1.0 + newBrightness); - - // TODO: round power to make nice numbers - // ... - - if (power > 1.0) return Lighter(color, power); - else return Darker(color, power); - } - - /// - /// Makes gradient colors from lighter to darker - /// - /// Base color - /// Count of items in the gradient - /// Colors from lighter to darker - static Color[] GetGradient(Color color, int count) - { - const double lowBrightness = 0.15; - const double highBrightness = 0.85; - Color[] result = new Color[count]; - - for (int i = 0; i < count; i++) - { - double brightness = lowBrightness + ((double)i) * (highBrightness - lowBrightness) / (double)count; - result[count - i - 1] = Rebright(color, brightness); - } - - return result; - } - - - #endregion - } +namespace Fluent +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Collections.ObjectModel; + using System.Collections.Specialized; + using System.Diagnostics; + using System.Runtime.InteropServices; + using System.Windows; + using System.Windows.Controls; + using System.Windows.Interop; + using System.Windows.Markup; + using System.Windows.Media; + using System.Windows.Threading; + + /// + /// Represents color gallery modes + /// + public enum ColorGalleryMode + { + /// + /// Color gallery displays only fixed highlight colors + /// + HighlightColors = 0, + + /// + /// Color gallery displays only fixed standart colors + /// + StandardColors, + + /// + /// Color gallery displays theme colors + /// + ThemeColors + } + + /// + /// Date template selector for gradients + /// + public class ColorGradientItemTemplateSelector : DataTemplateSelector + { + /// + /// When overridden in a derived class, returns a based on custom logic. + /// + /// + /// Returns a or null. The default value is null. + /// + /// The data object for which to select the template.The data-bound object. + public override DataTemplate SelectTemplate(object item, DependencyObject container) + { + if (item == null) + { + return null; + } + + ListBox listBox = null; + var parent = container; + while (parent != null) + { + parent = VisualTreeHelper.GetParent(parent); + listBox = parent as ListBox; + if (listBox != null) + { + break; + } + } + + if (listBox == null) + { + return null; + } + + ColorGallery colorGallery = null; + while (parent != null) + { + parent = VisualTreeHelper.GetParent(parent); + colorGallery = parent as ColorGallery; + if (colorGallery != null) break; + } + + if (colorGallery == null) + { + return null; + } + + var index = listBox.Items.IndexOf(item); + if (index < colorGallery.Columns) + { + return listBox.TryFindResource("GradientColorTopDataTemplate") as DataTemplate; + } + + if (index >= listBox.Items.Count - colorGallery.Columns) + { + return listBox.TryFindResource("GradientColorBottomDataTemplate") as DataTemplate; + } + + return listBox.TryFindResource("GradientColorCenterDataTemplate") as DataTemplate; + } + + } + + /// + /// More colors event args + /// + public class MoreColorsExecutingEventArgs : EventArgs + { + /// + /// Gets or sets choosed color + /// + public Color Color { get; set; } + /// + /// Gets or sets a value indicating whether more colors is canceled + /// + public bool Canceled { get; set; } + } + + /// + /// Represents color gallery + /// + [ContentProperty("ThemeColors")] + public class ColorGallery : Control + { + #region Constants + + /// + /// Hightlight colors array + /// + public static readonly Color[] HighlightColors = new Color[] + { + Color.FromRgb(0xFF ,0xFF ,0x00), + Color.FromRgb(0x00 ,0xFF ,0x00), + Color.FromRgb(0x00 ,0xFF ,0xFF), + + Color.FromRgb(0xFF ,0x00 ,0xFF), + Color.FromRgb(0x00 ,0x00 ,0xFF), + Color.FromRgb(0xFF ,0x00 ,0x00), + + Color.FromRgb(0x00 ,0x00 ,0x80), + Color.FromRgb(0x00 ,0x80 ,0x80), + Color.FromRgb(0x00 ,0x80 ,0x00), + + Color.FromRgb(0x80 ,0x00 ,0x80), + Color.FromRgb(0x80 ,0x00 ,0x00), + Color.FromRgb(0x80 ,0x80 ,0x00), + + Color.FromRgb(0x80 ,0x80 ,0x80), + Color.FromRgb(0xC0 ,0xC0 ,0xC0), + Color.FromRgb(0x00 ,0x00 ,0x00), + }; + + /// + /// Standard colors array + /// + public static readonly Color[] StandardColors = new Color[] + { + Color.FromRgb(0xFF ,0xFF ,0xFF), + Color.FromRgb(0xFF ,0x00 ,0x00), + Color.FromRgb(0xC0 ,0x50 ,0x4D), + Color.FromRgb(0xD1 ,0x63 ,0x49), + Color.FromRgb(0xDD ,0x84 ,0x84), + + Color.FromRgb(0xCC ,0xCC ,0xCC), + Color.FromRgb(0xFF ,0xC0 ,0x00), + Color.FromRgb(0xF7 ,0x96 ,0x46), + Color.FromRgb(0xD1 ,0x90 ,0x49), + Color.FromRgb(0xF3 ,0xA4 ,0x47), + + Color.FromRgb(0xA5 ,0xA5 ,0xA5), + Color.FromRgb(0xFF ,0xFF ,0x00), + Color.FromRgb(0x9B ,0xBB ,0x59), + Color.FromRgb(0xCC ,0xB4 ,0x00), + Color.FromRgb(0xDF ,0xCE ,0x04), + + Color.FromRgb(0x66 ,0x66 ,0x66), + Color.FromRgb(0x00 ,0xB0 ,0x50), + Color.FromRgb(0x4B ,0xAC ,0xC6), + Color.FromRgb(0x8F ,0xB0 ,0x8C), + Color.FromRgb(0xA5 ,0xB5 ,0x92), + + Color.FromRgb(0x33 ,0x33 ,0x33), + Color.FromRgb(0x00 ,0x4D ,0xBB), + Color.FromRgb(0x4F ,0x81 ,0xBD), + Color.FromRgb(0x64 ,0x6B ,0x86), + Color.FromRgb(0x80 ,0x9E ,0xC2), + + Color.FromRgb(0x00 ,0x00 ,0x00), + Color.FromRgb(0x9B ,0x00 ,0xD3), + Color.FromRgb(0x80 ,0x64 ,0xA2), + Color.FromRgb(0x9E ,0x7C ,0x7C), + Color.FromRgb(0x9C ,0x85 ,0xC0), + }; + + /// + /// Standard colors array in ThemeColor mode + /// + public static readonly Color[] StandardThemeColors = new Color[] + { + Color.FromRgb(0xC0 ,0x00 ,0x00), + Color.FromRgb(0xFF ,0x00 ,0x00), + Color.FromRgb(0xFF ,0xC0 ,0x00), + Color.FromRgb(0xFF ,0xFF ,0x00), + Color.FromRgb(0x92 ,0xD0 ,0x50), + Color.FromRgb(0x00 ,0xB0 ,0x50), + Color.FromRgb(0x00 ,0xB0 ,0xF0), + Color.FromRgb(0x00 ,0x70 ,0xC0), + Color.FromRgb(0x00 ,0x20 ,0x60), + Color.FromRgb(0x70 ,0x30 ,0xA0), + }; + + #endregion + + #region RecentItems + + private static ObservableCollection recentColors; + + /// + /// Gets recent colors collection + /// + public static ObservableCollection RecentColors + { + get + { + if (recentColors == null) recentColors = new ObservableCollection(); + return recentColors; + } + } + + #endregion + + #region Fields + + private MenuItem noColorButton; + private MenuItem automaticButton; + + private MenuItem moreColorsButton; + + private ListBox themeColorsListBox; + private ListBox themeGradientsListBox; + private ListBox standardColorsListBox; + private ListBox standardGradientsListBox; + private ListBox recentColorsListBox; + + private readonly List listBoxes = new List(); + + private bool isSelectionChanging; + + private bool isTemplateApplied; + + + #endregion + + #region Properties + + #region Mode + + /// + /// Gets or sets color gallery mode + /// + public ColorGalleryMode Mode + { + get { return (ColorGalleryMode)this.GetValue(ModeProperty); } + set { this.SetValue(ModeProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Mode. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty ModeProperty = + DependencyProperty.Register("Mode", typeof(ColorGalleryMode), typeof(ColorGallery), new UIPropertyMetadata(ColorGalleryMode.StandardColors, OnModeChanged)); + + private static void OnModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + (d as ColorGallery).UpdateGradients(); + } + + #endregion + + #region Chip Size + + /// + /// Gets or sets chip width + /// + public double ChipWidth + { + get { return (double)this.GetValue(ChipWidthProperty); } + set { this.SetValue(ChipWidthProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for ChipWidth. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty ChipWidthProperty = + DependencyProperty.Register("ChipWidth", typeof(double), typeof(ColorGallery), new UIPropertyMetadata(13.0, null, CoerceChipSize)); + + private static object CoerceChipSize(DependencyObject d, object basevalue) + { + double value = (double)basevalue; + if (value < 0) return 0; + return basevalue; + } + + /// + /// Gets or sets chip height + /// + public double ChipHeight + { + get { return (double)this.GetValue(ChipHeightProperty); } + set { this.SetValue(ChipHeightProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for ChipHeight. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty ChipHeightProperty = + DependencyProperty.Register("ChipHeight", typeof(double), typeof(ColorGallery), new UIPropertyMetadata(13.0, null, CoerceChipSize)); + + #endregion + + #region IsAutomaticColorButtonVisible + + /// + /// Gets or sets a value indicating whether Automatic button is visible + /// + public bool IsAutomaticColorButtonVisible + { + get { return (bool)this.GetValue(IsAutomaticColorButtonVisibleProperty); } + set { this.SetValue(IsAutomaticColorButtonVisibleProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for IsAutomaticColorButtonVisible. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IsAutomaticColorButtonVisibleProperty = + DependencyProperty.Register("IsAutomaticColorButtonVisible", typeof(bool), typeof(ColorGallery), new UIPropertyMetadata(true)); + + #endregion + + #region IsNoColorButtonVisible + + /// + /// Gets or sets a value indicating whether No color button is visible + /// + public bool IsNoColorButtonVisible + { + get { return (bool)this.GetValue(IsNoColorButtonVisibleProperty); } + set { this.SetValue(IsNoColorButtonVisibleProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for IsNoColorButtonVisible. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IsNoColorButtonVisibleProperty = + DependencyProperty.Register("IsNoColorButtonVisible", typeof(bool), typeof(ColorGallery), new UIPropertyMetadata(true)); + + #endregion + + #region IsMoreColorsButtonVisible + + /// + /// Gets or sets a value indicating whether More Colors button is visible + /// + public bool IsMoreColorsButtonVisible + { + get { return (bool)this.GetValue(IsMoreColorsButtonVisibleProperty); } + set { this.SetValue(IsMoreColorsButtonVisibleProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for IsMoreColorsButtonVisible. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IsMoreColorsButtonVisibleProperty = + DependencyProperty.Register("IsMoreColorsButtonVisible", typeof(bool), typeof(ColorGallery), new UIPropertyMetadata(true)); + + #endregion + + #region IsRecentColorsVisible + + /// + /// Gets or set a value indicating whether recent colors group displayed. Work only when Mode is ThemeColors + /// + public bool IsRecentColorsVisible + { + get { return (bool)this.GetValue(IsRecentColorsVisibleProperty); } + set { this.SetValue(IsRecentColorsVisibleProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for IsRecentColorsVisible. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IsRecentColorsVisibleProperty = + DependencyProperty.Register("IsRecentColorsVisible", typeof(bool), typeof(ColorGallery), new UIPropertyMetadata(true)); + + + + #endregion + + #region Columns + + /// + /// Gets or sets number of color gallery columns. It works only when Mode is ThemeColors + /// + public int Columns + { + get { return (int)this.GetValue(ColumnsProperty); } + set { this.SetValue(ColumnsProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Columns. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty ColumnsProperty = + DependencyProperty.Register("Columns", typeof(int), typeof(ColorGallery), new UIPropertyMetadata(10, OnColumnsChanged, CoerceColumns)); + + private static object CoerceColumns(DependencyObject d, object basevalue) + { + int value = (int)basevalue; + if (value < 1) return 1; + return basevalue; + } + + private static void OnColumnsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + (d as ColorGallery).UpdateGradients(); + } + + #endregion + + #region StandardColorGridRows + + /// + /// Gets or set number of standard color rows. Work only when Mode is ThemeColors + /// + public int StandardColorGridRows + { + get { return (int)this.GetValue(StandardColorGridRowsProperty); } + set { this.SetValue(StandardColorGridRowsProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for StandardColorGridRows. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty StandardColorGridRowsProperty = + DependencyProperty.Register("StandardColorGridRows", typeof(int), typeof(ColorGallery), new UIPropertyMetadata(0, OnStandardColorGridRowsChanged, CoeceGridRows)); + + private static object CoeceGridRows(DependencyObject d, object basevalue) + { + int value = (int)basevalue; + if (value < 0) return 0; + return basevalue; + } + + private static void OnStandardColorGridRowsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + (d as ColorGallery).UpdateGradients(); + } + + #endregion + + #region ThemeColorGridRows + + /// + /// Gets or set number of theme color rows. Work only when Mode is ThemeColors + /// + public int ThemeColorGridRows + { + get { return (int)this.GetValue(ThemeColorGridRowsProperty); } + set { this.SetValue(ThemeColorGridRowsProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for ThemeColorGridRows. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty ThemeColorGridRowsProperty = + DependencyProperty.Register("ThemeColorGridRows", typeof(int), typeof(ColorGallery), new UIPropertyMetadata(0, OnThemeColorGridRowsChanged, CoeceGridRows)); + + private static void OnThemeColorGridRowsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + (d as ColorGallery).UpdateGradients(); + } + + #endregion + + #region SelectedColor + + /// + /// Gets or sets selected color + /// + public Color? SelectedColor + { + get { return (Color?)this.GetValue(SelectedColorProperty); } + set { this.SetValue(SelectedColorProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for SelectedColor. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty SelectedColorProperty = + DependencyProperty.Register("SelectedColor", typeof(Color?), typeof(ColorGallery), new UIPropertyMetadata(null, OnSelectedColorChanged)); + + // Handles selected color changed + private static void OnSelectedColorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var gallery = d as ColorGallery; + + if (gallery == null) + { + return; + } + + // Raise event + gallery.RaiseEvent(new RoutedEventArgs(SelectedColorChangedEvent)); + + // Set color in gallery + var color = (Color?)e.NewValue; + gallery.UpdateSelectedColor(color); + } + + private void UpdateSelectedColor(Color? color) + { + if (this.isSelectionChanging + || this.isTemplateApplied == false) + { + return; + } + + this.isSelectionChanging = true; + var isSetted = false; + + // Check menu items + if (color.HasValue == false) + { + isSetted = true; + this.automaticButton.IsChecked = true; + this.noColorButton.IsChecked = false; + } + else if (color.Value == Colors.Transparent) + { + isSetted = true; + this.automaticButton.IsChecked = false; + this.noColorButton.IsChecked = true; + } + else + { + this.automaticButton.IsChecked = false; + this.noColorButton.IsChecked = false; + } + + // Remove selection from others + for (var i = 0; i < this.listBoxes.Count; i++) + { + if (isSetted == false + && this.listBoxes[i].Visibility == Visibility.Visible) + { + if (this.listBoxes[i].Items.Contains(color.Value)) + { + this.listBoxes[i].SelectedItem = color.Value; + isSetted = true; + } + } + else + { + this.listBoxes[i].SelectedItem = null; + } + } + + this.isSelectionChanging = false; + } + + #endregion + + #region ThemeColors + + private ObservableCollection themeColors; + + /// + /// Gets collection of theme colors + /// + public ObservableCollection ThemeColors + { + get + { + if (this.themeColors == null) + { + this.themeColors = new ObservableCollection(); + this.themeColors.CollectionChanged += this.OnThemeColorsChanged; + } + return this.themeColors; + } + } + + private void OnThemeColorsChanged(object sender, NotifyCollectionChangedEventArgs e) + { + this.UpdateGradients(); + } + + /// + /// Gets or sets theme colors source + /// + public IEnumerable ThemeColorsSource + { + get { return (IEnumerable)this.GetValue(ThemeColorsSourceProperty); } + set { this.SetValue(ThemeColorsSourceProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for ThemeColorsSource. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty ThemeColorsSourceProperty = + DependencyProperty.Register("ThemeColorsSource", typeof(IEnumerable), typeof(ColorGallery), new UIPropertyMetadata(null, OnThemeColorsSourceChanged)); + + private static void OnThemeColorsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ColorGallery gal = d as ColorGallery; + gal.ThemeColors.Clear(); + if (e.NewValue != null) + { + foreach (Color color in (e.NewValue as IEnumerable)) + { + gal.ThemeColors.Add(color); + } + } + } + + #endregion + + #region ThemeGradients + + /// + /// Gets theme gradients collection + /// + public Color[] ThemeGradients + { + get { return (Color[])this.GetValue(ThemeGradientsProperty); } + private set { this.SetValue(ThemeGradientsPropertyKey, value); } + } + + // + private static readonly DependencyPropertyKey ThemeGradientsPropertyKey = + DependencyProperty.RegisterReadOnly("ThemeGradients", typeof(Color[]), typeof(ColorGallery), new UIPropertyMetadata(null)); + + /// + /// Using a DependencyProperty as the backing store for ThemeGradients. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty ThemeGradientsProperty = ThemeGradientsPropertyKey.DependencyProperty; + + #endregion + + #region StandardGradients + + /// + /// Gets standart gradients collection + /// + public Color[] StandardGradients + { + get { return (Color[])this.GetValue(StandardGradientsProperty); } + private set { this.SetValue(StandardGradientsPropertyKey, value); } + } + + + private static readonly DependencyPropertyKey StandardGradientsPropertyKey = + DependencyProperty.RegisterReadOnly("StandardGradients", typeof(Color[]), typeof(ColorGallery), new UIPropertyMetadata(null)); + + /// + /// Using a DependencyProperty as the backing store for ThemeGradients. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty StandardGradientsProperty = StandardGradientsPropertyKey.DependencyProperty; + + #endregion + + #endregion + + #region Events + + /// + /// Occurs when selection color is changed + /// + public event RoutedEventHandler SelectedColorChanged + { + add + { + this.AddHandler(SelectedColorChangedEvent, value); + } + remove + { + this.RemoveHandler(SelectedColorChangedEvent, value); + } + } + /// + /// Identifies the SelectedColorChanged routed event. + /// + public static readonly RoutedEvent SelectedColorChangedEvent = EventManager.RegisterRoutedEvent("SelectedColorChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(ColorGallery)); + + /// + /// Raises SelectedColorChanged event + /// + public void RaiseSelectedColorChanged() + { + this.RaiseEvent(new RoutedEventArgs(SelectedColorChangedEvent, this)); + } + + /// + /// Occurs whether more colors menu item is clicked + /// + public event EventHandler MoreColorsExecuting; + + #endregion + + #region Initializing + + /// + /// Static constructor + /// + static ColorGallery() + { + Type type = typeof(ColorGallery); + DefaultStyleKeyProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(type)); + ContextMenuService.Attach(type); + StyleProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceStyle))); + } + + // Coerce object style + private static object OnCoerceStyle(DependencyObject d, object basevalue) + { + if (basevalue == null) + { + basevalue = (d as FrameworkElement).TryFindResource(typeof(ColorGallery)); + } + + return basevalue; + } + + /// + /// Default constructor + /// + public ColorGallery() + { + } + + #endregion + + #region Overrides + + /// + /// When overridden in a derived class, is invoked whenever application code or internal processes call . + /// + public override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + if (this.moreColorsButton != null) + this.moreColorsButton.Click += this.OnMoreColorsClick; + this.moreColorsButton = this.GetTemplateChild("PART_MoreColors") as MenuItem; + if (this.moreColorsButton != null) + this.moreColorsButton.Click += this.OnMoreColorsClick; + + if (this.noColorButton != null) + this.noColorButton.Click -= this.OnNoColorClick; + this.noColorButton = this.GetTemplateChild("PART_NoColor") as MenuItem; + if (this.noColorButton != null) + this.noColorButton.Click += this.OnNoColorClick; + + if (this.automaticButton != null) + this.automaticButton.Click -= this.OnAutomaticClick; + this.automaticButton = this.GetTemplateChild("PART_AutomaticColor") as MenuItem; + if (this.automaticButton != null) + this.automaticButton.Click += this.OnAutomaticClick; + + // List boxes + this.listBoxes.Clear(); + + if (this.themeColorsListBox != null) + this.themeColorsListBox.SelectionChanged -= this.OnListBoxSelectedChanged; + this.themeColorsListBox = this.GetTemplateChild("PART_ThemeColorsListBox") as ListBox; + this.listBoxes.Add(this.themeColorsListBox); + if (this.themeColorsListBox != null) + this.themeColorsListBox.SelectionChanged += this.OnListBoxSelectedChanged; + + if (this.themeGradientsListBox != null) + this.themeGradientsListBox.SelectionChanged -= this.OnListBoxSelectedChanged; + this.themeGradientsListBox = this.GetTemplateChild("PART_ThemeGradientColorsListBox") as ListBox; + this.listBoxes.Add(this.themeGradientsListBox); + if (this.themeGradientsListBox != null) + this.themeGradientsListBox.SelectionChanged += this.OnListBoxSelectedChanged; + + if (this.standardColorsListBox != null) + this.standardColorsListBox.SelectionChanged -= this.OnListBoxSelectedChanged; + this.standardColorsListBox = this.GetTemplateChild("PART_StandardColorsListBox") as ListBox; + this.listBoxes.Add(this.standardColorsListBox); + if (this.standardColorsListBox != null) + this.standardColorsListBox.SelectionChanged += this.OnListBoxSelectedChanged; + + if (this.standardGradientsListBox != null) + this.standardGradientsListBox.SelectionChanged -= this.OnListBoxSelectedChanged; + this.standardGradientsListBox = this.GetTemplateChild("PART_StandardGradientColorsListBox") as ListBox; + this.listBoxes.Add(this.standardGradientsListBox); + if (this.standardGradientsListBox != null) + this.standardGradientsListBox.SelectionChanged += this.OnListBoxSelectedChanged; + + if (this.recentColorsListBox != null) + this.recentColorsListBox.SelectionChanged -= this.OnListBoxSelectedChanged; + this.recentColorsListBox = this.GetTemplateChild("PART_RecentColorsListBox") as ListBox; + this.listBoxes.Add(this.recentColorsListBox); + if (this.recentColorsListBox != null) + this.recentColorsListBox.SelectionChanged += this.OnListBoxSelectedChanged; + + this.isTemplateApplied = true; + + this.UpdateSelectedColor(this.SelectedColor); + } + + #endregion + + #region Private Methods + + private static IntPtr customColors = IntPtr.Zero; + private readonly int[] colorsArray = new int[16]; + + private void OnMoreColorsClick(object sender, RoutedEventArgs e) + { + if (this.MoreColorsExecuting != null) + { + MoreColorsExecutingEventArgs args = new MoreColorsExecutingEventArgs(); + this.MoreColorsExecuting(this, args); + if (!args.Canceled) + { + Color color = args.Color; + if (RecentColors.Contains(color)) RecentColors.Remove(color); + RecentColors.Insert(0, color); + this.recentColorsListBox.SelectedIndex = 0; + } + } + else + { + NativeMethods.CHOOSECOLOR chooseColor = new NativeMethods.CHOOSECOLOR(); + Window wnd = Window.GetWindow(this); + if (wnd != null) chooseColor.hwndOwner = new WindowInteropHelper(wnd).Handle; + chooseColor.Flags = NativeMethods.CC_ANYCOLOR; + if (customColors == IntPtr.Zero) + { + // Set custom colors) + for (int i = 0; i < this.colorsArray.Length; i++) + this.colorsArray[i] = 0x00FFFFFF; + customColors = GCHandle.Alloc(this.colorsArray, GCHandleType.Pinned).AddrOfPinnedObject(); + } + chooseColor.lpCustColors = customColors; + if (NativeMethods.ChooseColor(chooseColor)) + { + Color color = ConvertFromWin32Color(chooseColor.rgbResult); + if (RecentColors.Contains(color)) RecentColors.Remove(color); + RecentColors.Insert(0, color); + this.recentColorsListBox.SelectedIndex = 0; + } + } + } + + private static Color ConvertFromWin32Color(int color) + { + int r = color & 0x000000FF; + int g = (color & 0x0000FF00) >> 8; + int b = (color & 0x00FF0000) >> 16; + return Color.FromArgb(255, (byte)r, (byte)g, (byte)b); + } + + private void OnAutomaticClick(object sender, RoutedEventArgs e) + { + this.isSelectionChanging = true; + this.noColorButton.IsChecked = false; + this.automaticButton.IsChecked = true; + // Remove selection from listboxes + for (int i = 0; i < this.listBoxes.Count; i++) + { + this.listBoxes[i].SelectedItem = null; + } + + this.SelectedColor = null; + this.isSelectionChanging = false; + } + + private void OnNoColorClick(object sender, RoutedEventArgs e) + { + this.isSelectionChanging = true; + this.noColorButton.IsChecked = true; + this.automaticButton.IsChecked = false; + // Remove selection from listboxes + for (int i = 0; i < this.listBoxes.Count; i++) + { + this.listBoxes[i].SelectedItem = null; + } + + this.SelectedColor = Colors.Transparent; + this.isSelectionChanging = false; + } + + private void OnListBoxSelectedChanged(object sender, SelectionChangedEventArgs e) + { + if (this.isSelectionChanging) return; + this.isSelectionChanging = true; + if (e.AddedItems != null && e.AddedItems.Count > 0) + { + // Remove selection from others + this.noColorButton.IsChecked = false; + this.automaticButton.IsChecked = false; + for (int i = 0; i < this.listBoxes.Count; i++) + { + if (this.listBoxes[i] != sender) + this.listBoxes[i].SelectedItem = null; + } + this.SelectedColor = (Color)e.AddedItems[0]; + PopupService.RaiseDismissPopupEvent(this, DismissPopupMode.Always); + } + this.isSelectionChanging = false; + } + + private void UpdateGradients() + { + if ((this.Mode == ColorGalleryMode.ThemeColors) && (this.Columns > 0)) + { + if (this.ThemeColorGridRows > 0) + { + this.ThemeGradients = this.GenerateThemeGradients(); + } + else + this.ThemeGradients = null; + + if (this.StandardColorGridRows > 0) + { + this.StandardGradients = this.GenerateStandardGradients(); + } + else + this.StandardGradients = null; + } + else + { + this.StandardGradients = null; + this.ThemeGradients = null; + } + } + + private Color[] GenerateStandardGradients() + { + int count = Math.Min(this.Columns, StandardThemeColors.Length); + Color[] result = new Color[this.Columns *this.StandardColorGridRows]; + for (int i = 0; i < count; i++) + { + Color[] colors = GetGradient(StandardThemeColors[i], this.StandardColorGridRows); + for (int j = 0; j < this.StandardColorGridRows; j++) + { + result[i + j *this.Columns] = colors[j]; + } + } + return result; + } + + private Color[] GenerateThemeGradients() + { + int count = Math.Min(this.Columns, this.ThemeColors.Count); + Color[] result = new Color[this.Columns *this.ThemeColorGridRows]; + for (int i = 0; i < count; i++) + { + Color[] colors = GetGradient(this.ThemeColors[i], this.ThemeColorGridRows); + for (int j = 0; j < this.ThemeColorGridRows; j++) + { + result[i + j *this.Columns] = colors[j]; + } + } + return result; + } + + #endregion + + #region Gradient Generation + + /// + /// Returns brightness of the given color from 0..1 + /// + /// Color + /// Brightness of the given color from 0..1 + static double GetBrightness(Color color) + { + double summ = ((double)color.R) + ((double)color.G) + ((double)color.B); + return summ / (255.0 * 3.0); + } + + // Makes the given color lighter + static Color Lighter(Color color, double power) + { + double totalAvailability = 255.0 * 3.0 - (double)color.R + (double)color.G + (double)color.B; + double redAvailability; + double greenAvailability; + double blueAvailability; + double needToBeAdded; + + if (color.R + color.G + color.B == 0) + { + redAvailability = 1.0 / 3.0; + greenAvailability = 1.0 / 3.0; + blueAvailability = 1.0 / 3.0; + needToBeAdded = power * 255.0 * 3.0; + } + else + { + redAvailability = (255.0 - (double)color.R) / totalAvailability; + greenAvailability = (255.0 - (double)color.G) / totalAvailability; + blueAvailability = (255.0 - (double)color.B) / totalAvailability; + needToBeAdded = ((double)color.R + (double)color.G + (double)color.B) * (power - 1); + } + + + Color result = Color.FromRgb( + (byte)(color.R + ((byte)(redAvailability * needToBeAdded))), + (byte)(color.G + ((byte)(greenAvailability * needToBeAdded))), + (byte)(color.B + ((byte)(blueAvailability * needToBeAdded)))); + + return result; + } + + // Makes the given color darker + static Color Darker(Color color, double power) + { + double totalAvailability = (double)color.R + (double)color.G + (double)color.B; + double redAvailability = ((double)color.R) / totalAvailability; + double greenAvailability = ((double)color.G) / totalAvailability; + double blueAvailability = ((double)color.B) / totalAvailability; + + double needToBeAdded = ((double)color.R + (double)color.G + (double)color.B); + needToBeAdded = needToBeAdded - needToBeAdded * power; + + Color result = Color.FromRgb( + (byte)(color.R - ((byte)(redAvailability * needToBeAdded))), + (byte)(color.G - ((byte)(greenAvailability * needToBeAdded))), + (byte)(color.B - ((byte)(blueAvailability * needToBeAdded)))); + + return result; + } + + // Makes a new color from the given with new brightness + static Color Rebright(Color color, double newBrightness) + { + double currentBrightness = GetBrightness(color); + double power = currentBrightness != 0.0 ? newBrightness / currentBrightness : (1.0 + newBrightness); + + // TODO: round power to make nice numbers + // ... + + if (power > 1.0) return Lighter(color, power); + else return Darker(color, power); + } + + /// + /// Makes gradient colors from lighter to darker + /// + /// Base color + /// Count of items in the gradient + /// Colors from lighter to darker + static Color[] GetGradient(Color color, int count) + { + const double lowBrightness = 0.15; + const double highBrightness = 0.85; + Color[] result = new Color[count]; + + for (int i = 0; i < count; i++) + { + double brightness = lowBrightness + ((double)i) * (highBrightness - lowBrightness) / (double)count; + result[count - i - 1] = Rebright(color, brightness); + } + + return result; + } + + + #endregion + } } \ No newline at end of file diff --git a/Fluent/Controls/ComboBox.cs b/Fluent.Ribbon/Controls/ComboBox.cs similarity index 97% rename from Fluent/Controls/ComboBox.cs rename to Fluent.Ribbon/Controls/ComboBox.cs index 565215685..61cc796dc 100644 --- a/Fluent/Controls/ComboBox.cs +++ b/Fluent.Ribbon/Controls/ComboBox.cs @@ -1,971 +1,971 @@ -namespace Fluent -{ - using System; - using System.Diagnostics; - using System.Diagnostics.CodeAnalysis; - using System.Threading; - using System.Windows; - using System.Windows.Controls; - using System.Windows.Controls.Primitives; - using System.Windows.Data; - using System.Windows.Input; - using System.Windows.Media; - using System.Windows.Media.Imaging; - using System.Windows.Threading; - using Fluent.Internal; - - /// - /// Represents custom Fluent UI ComboBox - /// - [TemplatePart(Name = "PART_ResizeBothThumb", Type = typeof(Thumb))] - [TemplatePart(Name = "PART_ResizeVerticalThumb", Type = typeof(Thumb))] - public class ComboBox : System.Windows.Controls.ComboBox, IQuickAccessItemProvider, IRibbonControl, IDropDownControl - { - #region Fields - - // Thumb to resize in both directions - private Thumb resizeBothThumb; - // Thumb to resize vertical - private Thumb resizeVerticalThumb; - - private IInputElement focusedElement; - - private Panel menuPanel; - - private Border dropDownBorder; - private Border contentBorder; - - private ContentPresenter contentSite; - - // Freezed image (created during snapping) - private Image snappedImage; - - // Is visual currently snapped - private bool isSnapped; - - private GalleryPanel galleryPanel; - - private ScrollViewer scrollViewer; - - private bool canSizeY; - - #endregion - - #region Properties - - #region Size - - /// - /// Gets or sets Size for the element. - /// - public RibbonControlSize Size - { - get { return (RibbonControlSize)this.GetValue(SizeProperty); } - set { this.SetValue(SizeProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Size. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty SizeProperty = RibbonProperties.SizeProperty.AddOwner(typeof(ComboBox)); - - #endregion - - #region SizeDefinition - - /// - /// Gets or sets SizeDefinition for element. - /// - public RibbonControlSizeDefinition SizeDefinition - { - get { return (RibbonControlSizeDefinition)this.GetValue(SizeDefinitionProperty); } - set { this.SetValue(SizeDefinitionProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for SizeDefinition. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty SizeDefinitionProperty = RibbonProperties.SizeDefinitionProperty.AddOwner(typeof(ComboBox)); - - #endregion - - #region KeyTip - - /// - /// Gets or sets KeyTip for element. - /// - public string KeyTip - { - get { return (string)this.GetValue(KeyTipProperty); } - set { this.SetValue(KeyTipProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Keys. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty KeyTipProperty = Fluent.KeyTip.KeysProperty.AddOwner(typeof(ComboBox)); - - #endregion - - /// - /// Gets drop down popup - /// - public Popup DropDownPopup { get; private set; } - - /// - /// Gets a value indicating whether context menu is opened - /// - public bool IsContextMenuOpened { get; set; } - - #region Header - - /// - /// Gets or sets element Text - /// - public object Header - { - get { return (string)this.GetValue(HeaderProperty); } - set { this.SetValue(HeaderProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Header. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty HeaderProperty = RibbonControl.HeaderProperty.AddOwner(typeof(ComboBox)); - - #endregion - - #region Icon - - /// - /// Gets or sets Icon for the element - /// - public object Icon - { - get { return (ImageSource)this.GetValue(IconProperty); } - set { this.SetValue(IconProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Icon. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IconProperty = RibbonControl.IconProperty.AddOwner(typeof(ComboBox), new UIPropertyMetadata(null, OnIconChanged)); - - private static void OnIconChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var element = d as ComboBox; - var oldElement = e.OldValue as FrameworkElement; - if (oldElement != null) element.RemoveLogicalChild(oldElement); - var newElement = e.NewValue as FrameworkElement; - if (newElement != null) element.AddLogicalChild(newElement); - } - - #endregion - - #region Menu - - /// - /// Gets or sets menu to show in combo box bottom - /// - public RibbonMenu Menu - { - get { return (RibbonMenu)this.GetValue(MenuProperty); } - set { this.SetValue(MenuProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Menu. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty MenuProperty = - DependencyProperty.Register("Menu", typeof(RibbonMenu), typeof(ComboBox), new UIPropertyMetadata(null)); - - #endregion - - #region InputWidth - - /// - /// Gets or sets width of the value input part of combobox - /// - public double InputWidth - { - get { return (double)this.GetValue(InputWidthProperty); } - set { this.SetValue(InputWidthProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for InputWidth. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty InputWidthProperty = - DependencyProperty.Register("InputWidth", typeof(double), typeof(ComboBox), new UIPropertyMetadata(double.NaN)); - - #endregion - - #region ItemHeight - - /// - /// Gets or sets items height - /// - public double ItemHeight - { - get { return (double)this.GetValue(ItemHeightProperty); } - set { this.SetValue(ItemHeightProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for ItemHeight. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty ItemHeightProperty = - DependencyProperty.Register("ItemHeight", typeof(double), typeof(ComboBox), new UIPropertyMetadata(double.NaN)); - - #endregion - - #region GroupBy - - /// - /// Gets or sets name of property which - /// will use to group items in the ComboBox. - /// - public string GroupBy - { - get { return (string)this.GetValue(GroupByProperty); } - set { this.SetValue(GroupByProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for GroupBy. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty GroupByProperty = - DependencyProperty.Register("GroupBy", typeof(string), - typeof(ComboBox), new UIPropertyMetadata(null)); - - #endregion - - #region ResizeMode - - /// - /// Gets or sets context menu resize mode - /// - public ContextMenuResizeMode ResizeMode - { - get { return (ContextMenuResizeMode)this.GetValue(ResizeModeProperty); } - set { this.SetValue(ResizeModeProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for ResizeMode. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty ResizeModeProperty = - DependencyProperty.Register("ResizeMode", typeof(ContextMenuResizeMode), typeof(ComboBox), new UIPropertyMetadata(ContextMenuResizeMode.None)); - - #endregion - - #region Snapping - - /// - /// Snaps / Unsnaps the Visual - /// (remove visuals and substitute with freezed image) - /// - private bool IsSnapped - { - get { return this.isSnapped; } - set - { - if (value == this.isSnapped) return; - if (this.snappedImage == null) return; - if ((value) && (((int)this.contentSite.ActualWidth > 0) && ((int)this.contentSite.ActualHeight > 0))) - { - // Render the freezed image - RenderOptions.SetBitmapScalingMode(this.snappedImage, BitmapScalingMode.NearestNeighbor); - var renderTargetBitmap = new RenderTargetBitmap((int)this.contentSite.ActualWidth + (int)this.contentSite.Margin.Left + (int)this.contentSite.Margin.Right, - (int)this.contentSite.ActualHeight + (int)this.contentSite.Margin.Top + (int)this.contentSite.Margin.Bottom, 96, 96, - PixelFormats.Pbgra32); - renderTargetBitmap.Render(this.contentSite); - this.snappedImage.Source = renderTargetBitmap; - this.snappedImage.FlowDirection = this.FlowDirection; - /*snappedImage.Width = contentSite.ActualWidth; - snappedImage.Height = contentSite.ActualHeight;*/ - this.snappedImage.Visibility = Visibility.Visible; - this.contentSite.Visibility = Visibility.Hidden; - this.isSnapped = value; - } - else - { - this.snappedImage.Visibility = Visibility.Collapsed; - this.contentSite.Visibility = Visibility.Visible; - this.isSnapped = value; - } - - this.InvalidateVisual(); - } - } - - #endregion - - #region DropDownHeight - - /// - /// Gets or sets initial dropdown height - /// - public double DropDownHeight - { - get { return (double)this.GetValue(DropDownHeightProperty); } - set { this.SetValue(DropDownHeightProperty, value); } - } - - /// - /// /Using a DependencyProperty as the backing store for DropDownHeight. This enables animation, styling, binding, - /// etc... - /// - public static readonly DependencyProperty DropDownHeightProperty = - DependencyProperty.Register("InitialDropDownHeight", typeof(double), typeof(ComboBox), new UIPropertyMetadata(double.NaN)); - - #endregion - - #region ShowPopupOnTop - - /// - /// Gets a value indicating whether popup is shown on top; - /// - public bool ShowPopupOnTop - { - get { return (bool)this.GetValue(ShowPopupOnTopProperty); } - private set { this.SetValue(ShowPopupOnTopPropertyKey, value); } - } - - // - private static readonly DependencyPropertyKey ShowPopupOnTopPropertyKey = DependencyProperty.RegisterReadOnly("ShowPopupOnTop", typeof(bool), typeof(ComboBox), new UIPropertyMetadata(false)); - - /// - /// Using a DependencyProperty as the backing store for ShowPopupOnTop. This enables animation, styling, binding, - /// etc... - /// - public static readonly DependencyProperty ShowPopupOnTopProperty = ShowPopupOnTopPropertyKey.DependencyProperty; - - #endregion - - #endregion - - #region Constructors - - /// - /// Static constructor - /// - [SuppressMessage("Microsoft.Performance", "CA1810")] - static ComboBox() - { - var type = typeof(ComboBox); - ToolTipService.Attach(type); - PopupService.Attach(type); - ContextMenuService.Attach(type); - DefaultStyleKeyProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(type)); - SelectedItemProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(OnSelectionItemChanged, CoerceSelectedItem)); - } - - private static void OnSelectionItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var combo = d as ComboBox; - if (!combo.isQuickAccessOpened && !combo.isQuickAccessFocused && (combo.quickAccessCombo != null)) combo.UpdateQuickAccessCombo(); - } - - private static object CoerceSelectedItem(DependencyObject d, object basevalue) - { - var combo = d as ComboBox; - if (combo.isQuickAccessOpened || combo.isQuickAccessFocused) return combo.selectedItem; - return basevalue; - } - - /// - /// Default Constructor - /// - public ComboBox() - { - ContextMenuService.Coerce(this); - } - - #endregion - - #region QuickAccess - - /// - /// Gets control which represents shortcut item. - /// This item MUST be syncronized with the original - /// and send command to original one control. - /// - /// Control which represents shortcut item - public virtual FrameworkElement CreateQuickAccessItem() - { - var combo = new ComboBox(); - RibbonControl.BindQuickAccessItem(this, combo); - RibbonControl.Bind(this, combo, "GroupBy", GroupByProperty, BindingMode.OneWay); - RibbonControl.Bind(this, combo, "ActualWidth", WidthProperty, BindingMode.OneWay); - RibbonControl.Bind(this, combo, "InputWidth", InputWidthProperty, BindingMode.OneWay); - RibbonControl.Bind(this, combo, "ItemHeight", ItemHeightProperty, BindingMode.OneWay); - RibbonControl.Bind(this, combo, "IsEditable", IsEditableProperty, BindingMode.OneWay); - RibbonControl.Bind(this, combo, "IsReadOnly", IsReadOnlyProperty, BindingMode.OneWay); - RibbonControl.Bind(this, combo, "ResizeMode", ResizeModeProperty, BindingMode.OneWay); - RibbonControl.Bind(this, combo, "Text", TextProperty, BindingMode.TwoWay); - - RibbonControl.Bind(this, combo, "DisplayMemberPath", DisplayMemberPathProperty, BindingMode.OneWay); - RibbonControl.Bind(this, combo, "GroupStyleSelector", GroupStyleSelectorProperty, BindingMode.OneWay); - RibbonControl.Bind(this, combo, "ItemContainerStyle", ItemContainerStyleProperty, BindingMode.OneWay); - RibbonControl.Bind(this, combo, "ItemsPanel", ItemsPanelProperty, BindingMode.OneWay); - RibbonControl.Bind(this, combo, "ItemStringFormat", ItemStringFormatProperty, BindingMode.OneWay); - RibbonControl.Bind(this, combo, "ItemTemplate", ItemTemplateProperty, BindingMode.OneWay); - RibbonControl.Bind(this, combo, "SelectedValuePath", SelectedValuePathProperty, BindingMode.OneWay); - RibbonControl.Bind(this, combo, "MaxDropDownHeight", MaxDropDownHeightProperty, BindingMode.OneWay); - combo.DropDownOpened += this.OnQuickAccessOpened; - if (this.IsEditable) combo.GotFocus += this.OnQuickAccessTextBoxGetFocus; - this.quickAccessCombo = combo; - this.UpdateQuickAccessCombo(); - return combo; - } - - private void OnQuickAccessTextBoxGetFocus(object sender, RoutedEventArgs e) - { - this.isQuickAccessFocused = true; - if (!this.isQuickAccessOpened) this.Freeze(); - this.quickAccessCombo.LostFocus += this.OnQuickAccessTextBoxLostFocus; - } - - private void OnQuickAccessTextBoxLostFocus(object sender, RoutedEventArgs e) - { - this.quickAccessCombo.LostFocus -= this.OnQuickAccessTextBoxLostFocus; - if (!this.isQuickAccessOpened) this.Unfreeze(); - this.isQuickAccessFocused = false; - } - - private bool isQuickAccessFocused; - private bool isQuickAccessOpened; - private object selectedItem; - private ComboBox quickAccessCombo; - - private void OnQuickAccessOpened(object sender, EventArgs e) - { - this.isQuickAccessOpened = true; - this.quickAccessCombo.DropDownClosed += this.OnQuickAccessMenuClosed; - this.quickAccessCombo.UpdateLayout(); - if (!this.isQuickAccessFocused) - this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, ((ThreadStart)(() => - { - this.Freeze(); - this.Dispatcher.BeginInvoke(DispatcherPriority.Input, ((ThreadStart)(() => { if (this.quickAccessCombo.SelectedItem != null) (this.quickAccessCombo.ItemContainerGenerator.ContainerFromItem(this.quickAccessCombo.SelectedItem) as ComboBoxItem).BringIntoView(); } - ))); - } - ))); - } - - private void OnQuickAccessMenuClosed(object sender, EventArgs e) - { - this.quickAccessCombo.DropDownClosed -= this.OnQuickAccessMenuClosed; - if (!this.isQuickAccessFocused) this.Unfreeze(); - this.isQuickAccessOpened = false; - } - - private void Freeze() - { - this.IsSnapped = true; - this.selectedItem = this.SelectedItem; - - ItemsControlHelper.MoveItemsToDifferentControl(this, this.quickAccessCombo); - - this.SelectedItem = null; - this.quickAccessCombo.SelectedItem = this.selectedItem; - this.quickAccessCombo.Menu = this.Menu; - this.Menu = null; - this.quickAccessCombo.IsSnapped = false; - } - - private void Unfreeze() - { - var text = this.quickAccessCombo.Text; - this.selectedItem = this.quickAccessCombo.SelectedItem; - this.quickAccessCombo.IsSnapped = true; - - ItemsControlHelper.MoveItemsToDifferentControl(this.quickAccessCombo, this); - - this.quickAccessCombo.SelectedItem = null; - this.SelectedItem = this.selectedItem; - this.Menu = this.quickAccessCombo.Menu; - this.quickAccessCombo.Menu = null; - this.IsSnapped = false; - this.Text = text; - this.UpdateLayout(); - } - - private void UpdateQuickAccessCombo() - { - if (this.IsLoaded == false) - { - this.Loaded += this.OnFirstLoaded; - } - - if (this.IsEditable == false) - { - this.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, (ThreadStart)(() => - { - this.quickAccessCombo.IsSnapped = true; - this.IsSnapped = true; - if (this.snappedImage != null && - this.quickAccessCombo.snappedImage != null) - { - this.quickAccessCombo.snappedImage.Source = this.snappedImage.Source; - this.quickAccessCombo.snappedImage.Visibility = Visibility.Visible; - if (this.quickAccessCombo.IsSnapped == false) - { - this.quickAccessCombo.isSnapped = true; - } - } - this.IsSnapped = false; - })); - } - } - - private void OnFirstLoaded(object sender, RoutedEventArgs e) - { - this.Loaded -= this.OnFirstLoaded; - this.UpdateQuickAccessCombo(); - } - - /// - /// Gets or sets whether control can be added to quick access toolbar - /// - public bool CanAddToQuickAccessToolBar - { - get { return (bool)this.GetValue(CanAddToQuickAccessToolBarProperty); } - set { this.SetValue(CanAddToQuickAccessToolBarProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for CanAddToQuickAccessToolBar. This enables animation, styling, - /// binding, etc... - /// - public static readonly DependencyProperty CanAddToQuickAccessToolBarProperty = RibbonControl.CanAddToQuickAccessToolBarProperty.AddOwner(typeof(ComboBox), new UIPropertyMetadata(true, RibbonControl.OnCanAddToQuickAccessToolbarChanged)); - - #endregion - - #region Overrides - - /// - /// When overridden in a derived class, is invoked whenever application code or internal processes call - /// . - /// - public override void OnApplyTemplate() - { - this.DropDownPopup = this.GetTemplateChild("PART_Popup") as Popup; - - if (this.resizeVerticalThumb != null) - { - this.resizeVerticalThumb.DragDelta -= this.OnResizeVerticalDelta; - } - this.resizeVerticalThumb = this.GetTemplateChild("PART_ResizeVerticalThumb") as Thumb; - if (this.resizeVerticalThumb != null) - { - this.resizeVerticalThumb.DragDelta += this.OnResizeVerticalDelta; - } - - if (this.resizeBothThumb != null) - { - this.resizeBothThumb.DragDelta -= this.OnResizeBothDelta; - } - this.resizeBothThumb = this.GetTemplateChild("PART_ResizeBothThumb") as Thumb; - if (this.resizeBothThumb != null) - { - this.resizeBothThumb.DragDelta += this.OnResizeBothDelta; - } - - this.menuPanel = this.GetTemplateChild("PART_MenuPanel") as Panel; - - this.snappedImage = this.GetTemplateChild("PART_SelectedImage") as Image; - this.contentSite = this.GetTemplateChild("PART_ContentSite") as ContentPresenter; - - if (this.contentBorder != null) this.contentBorder.PreviewMouseDown -= this.OnContentBorderPreviewMouseDown; - this.contentBorder = this.GetTemplateChild("PART_ContentBorder") as Border; - if (this.contentBorder != null) this.contentBorder.PreviewMouseDown += this.OnContentBorderPreviewMouseDown; - - this.galleryPanel = this.GetTemplateChild("PART_GalleryPanel") as GalleryPanel; - this.scrollViewer = this.GetTemplateChild("PART_ScrollViewer") as ScrollViewer; - - this.dropDownBorder = this.GetTemplateChild("PART_DropDownBorder") as Border; - - base.OnApplyTemplate(); - } - - /// - /// Reports when a combo box's popup opens. - /// - /// The event data for the event. - protected override void OnDropDownOpened(EventArgs e) - { - base.OnDropDownOpened(e); - - Mouse.Capture(this, CaptureMode.SubTree); - - if (this.SelectedItem != null) - { - Keyboard.Focus(this.ItemContainerGenerator.ContainerFromItem(this.SelectedItem) as IInputElement); - } - - this.focusedElement = Keyboard.FocusedElement; - - if (this.focusedElement != null) - { - this.focusedElement.LostKeyboardFocus += this.OnFocusedElementLostKeyboardFocus; - } - - this.canSizeY = true; - - this.galleryPanel.Width = double.NaN; - this.scrollViewer.Height = double.NaN; - - var popupChild = this.DropDownPopup.Child as FrameworkElement; - var heightDelta = popupChild.DesiredSize.Height - this.scrollViewer.DesiredSize.Height; - - var initialHeight = Math.Min(RibbonControl.GetControlWorkArea(this).Height * 2 / 3, this.MaxDropDownHeight); - - if (double.IsNaN(this.DropDownHeight) == false) - { - initialHeight = Math.Min(this.DropDownHeight, this.MaxDropDownHeight); - } - - if (this.scrollViewer.DesiredSize.Height > initialHeight) - { - this.scrollViewer.Height = initialHeight; - } - else - { - initialHeight = this.scrollViewer.DesiredSize.Height; - } - - var monitor = RibbonControl.GetControlMonitor(this); - var delta = monitor.Bottom - this.PointToScreen(new Point()).Y - this.ActualHeight - initialHeight - heightDelta; - - if (delta >= 0) - { - this.ShowPopupOnTop = false; - } - else - { - var deltaTop = this.PointToScreen(new Point()).Y - initialHeight - heightDelta - monitor.Top; - - if (deltaTop > delta) - { - this.ShowPopupOnTop = true; - } - else - { - this.ShowPopupOnTop = false; - } - - if (deltaTop < 0) - { - delta = Math.Max(Math.Abs(delta), Math.Abs(deltaTop)); - - if (delta > this.galleryPanel.GetItemSize().Height) - { - this.scrollViewer.Height = delta; - } - else - { - this.canSizeY = false; - this.scrollViewer.Height = this.galleryPanel.GetItemSize().Height; - } - } - } - - popupChild.UpdateLayout(); - } - - /// - /// Reports when a combo box's popup closes. - /// - /// The event data for the event. - protected override void OnDropDownClosed(EventArgs e) - { - base.OnDropDownClosed(e); - if (Mouse.Captured == this) Mouse.Capture(null); - if (this.focusedElement != null) this.focusedElement.LostKeyboardFocus -= this.OnFocusedElementLostKeyboardFocus; - this.focusedElement = null; - this.ShowPopupOnTop = false; - this.galleryPanel.Width = double.NaN; - this.scrollViewer.Height = double.NaN; - } - - private void OnFocusedElementLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e) - { - if (this.focusedElement != null) this.focusedElement.LostKeyboardFocus -= this.OnFocusedElementLostKeyboardFocus; - this.focusedElement = Keyboard.FocusedElement; - if (this.focusedElement != null) - { - this.focusedElement.LostKeyboardFocus += this.OnFocusedElementLostKeyboardFocus; - if ((this.IsEditable) && - (this.Items.Contains(this.ItemContainerGenerator.ItemFromContainer(Keyboard.FocusedElement as DependencyObject)))) - { - this.SelectedItem = this.ItemContainerGenerator.ItemFromContainer(Keyboard.FocusedElement as DependencyObject); - } - } - } - - /// - /// Invoked when a attached routed event occurs. - /// - /// Event data. - protected override void OnPreviewKeyDown(KeyEventArgs e) - { - if ((this.IsEditable) && ((e.Key == Key.Down) || (e.Key == Key.Up)) && (!this.IsDropDownOpen)) - { - this.IsDropDownOpen = true; - e.Handled = true; - return; - } - - base.OnPreviewKeyDown(e); - } - - /// - /// Invoked when a attached routed event occurs. - /// - /// Event data. - protected override void OnKeyDown(KeyEventArgs e) - { - if (e.Key == Key.Down) - { - Debug.WriteLine("Down pressed. FocusedElement - " + Keyboard.FocusedElement); - if ((this.Menu != null) && this.Menu.Items.Contains(this.Menu.ItemContainerGenerator.ItemFromContainer(Keyboard.FocusedElement as DependencyObject))) - { - var indexOfMSelectedItem = this.Menu.ItemContainerGenerator.IndexFromContainer(Keyboard.FocusedElement as DependencyObject); - if (indexOfMSelectedItem != this.Menu.Items.Count - 1) - { - Keyboard.Focus(this.Menu.ItemContainerGenerator.ContainerFromIndex(indexOfMSelectedItem + 1) as IInputElement); - } - else - { - if ((this.Items.Count > 0) && (!this.IsEditable)) - { - Keyboard.Focus(this.ItemContainerGenerator.ContainerFromIndex(0) as IInputElement); - } - else Keyboard.Focus(this.Menu.Items[0] as IInputElement); - } - } - else if (this.Items.Contains(this.ItemContainerGenerator.ItemFromContainer(Keyboard.FocusedElement as DependencyObject))) - { - var indexOfSelectedItem = this.ItemContainerGenerator.IndexFromContainer(Keyboard.FocusedElement as DependencyObject); - if (indexOfSelectedItem != this.Items.Count - 1) - { - Keyboard.Focus(this.ItemContainerGenerator.ContainerFromIndex(indexOfSelectedItem + 1) as IInputElement); - } - else - { - if ((this.Menu != null) && (this.Menu.Items.Count > 0) && (!this.IsEditable)) Keyboard.Focus(this.Menu.ItemContainerGenerator.ContainerFromIndex(0) as IInputElement); - else - { - Keyboard.Focus(this.ItemContainerGenerator.ContainerFromIndex(0) as IInputElement); - } - } - } - else if (this.SelectedItem != null) Keyboard.Focus(this.ItemContainerGenerator.ContainerFromItem(this.SelectedItem) as IInputElement); - e.Handled = true; - Debug.WriteLine("FocusedElement - " + Keyboard.FocusedElement); - return; - } - else if (e.Key == Key.Up) - { - Debug.WriteLine("Up pressed. FocusedElement - " + Keyboard.FocusedElement); - if ((this.Menu != null) && this.Menu.Items.Contains(this.Menu.ItemContainerGenerator.ItemFromContainer(Keyboard.FocusedElement as DependencyObject))) - { - var indexOfMSelectedItem = this.Menu.ItemContainerGenerator.IndexFromContainer(Keyboard.FocusedElement as DependencyObject); - if (indexOfMSelectedItem != 0) - { - Keyboard.Focus(this.Menu.ItemContainerGenerator.ContainerFromIndex(indexOfMSelectedItem - 1) as IInputElement); - } - else - { - if ((this.Items.Count > 0) && (!this.IsEditable)) - { - Keyboard.Focus(this.ItemContainerGenerator.ContainerFromIndex(this.Items.Count - 1) as IInputElement); - } - else Keyboard.Focus(this.Menu.Items[this.Menu.Items.Count - 1] as IInputElement); - } - } - else if (this.Items.Contains(this.ItemContainerGenerator.ItemFromContainer(Keyboard.FocusedElement as DependencyObject))) - { - var indexOfSelectedItem = this.ItemContainerGenerator.IndexFromContainer(Keyboard.FocusedElement as DependencyObject); - if (indexOfSelectedItem != 0) - { - Keyboard.Focus(this.ItemContainerGenerator.ContainerFromIndex(indexOfSelectedItem - 1) as IInputElement); - } - else - { - if ((this.Menu != null) && (this.Menu.Items.Count > 0) && (!this.IsEditable)) Keyboard.Focus(this.Menu.ItemContainerGenerator.ContainerFromIndex(this.Menu.Items.Count - 1) as IInputElement); - else - { - Keyboard.Focus(this.ItemContainerGenerator.ContainerFromIndex(this.Items.Count - 1) as IInputElement); - } - } - } - else if (this.SelectedItem != null) Keyboard.Focus(this.ItemContainerGenerator.ContainerFromItem(this.SelectedItem) as IInputElement); - Debug.WriteLine("FocusedElement - " + Keyboard.FocusedElement); - e.Handled = true; - return; - } - else if ((e.Key == Key.Return) && (!this.IsEditable) && this.IsDropDownOpen) - { - var element = Keyboard.FocusedElement as DependencyObject; - - // only try to select if we got a focusedElement - if (element != null) - { - var newSelectedIndex = this.ItemContainerGenerator.IndexFromContainer(element); - - // only set the selected index if the focused element was in a container in this combobox - if (newSelectedIndex > -1) - { - this.SelectedIndex = newSelectedIndex; - } - } - } - base.OnKeyDown(e); - } - - #endregion - - #region Methods - - /// - /// Handles key tip pressed - /// - public virtual void OnKeyTipPressed() - { - this.Dispatcher.BeginInvoke( - DispatcherPriority.Normal, - (DispatcherOperationCallback)delegate(object arg) - { - var ctrl = (ComboBox)arg; - - // Edge case: Whole dropdown content is disabled - if (ctrl.IsKeyboardFocusWithin == false) - { - Keyboard.Focus(ctrl); - } - return null; - }, - this); - - if (!this.IsEditable) - { - this.IsDropDownOpen = true; - } - } - - /// - /// Handles back navigation with KeyTips - /// - public void OnKeyTipBack() - { - } - - #endregion - - #region Private methods - - // Prevent reopenning of the dropdown menu (popup) - private void OnContentBorderPreviewMouseDown(object sender, MouseButtonEventArgs e) - { - if (this.IsDropDownOpen) - { - this.IsDropDownOpen = false; - e.Handled = true; - } - } - - // Handles resize both drag - private void OnResizeBothDelta(object sender, DragDeltaEventArgs e) - { - // Set height - this.SetDragHeight(e); - - // Set width - this.menuPanel.Width = double.NaN; - if (double.IsNaN(this.galleryPanel.Width)) - { - this.galleryPanel.Width = this.galleryPanel.ActualWidth; - } - - var monitorRight = RibbonControl.GetControlMonitor(this).Right; - var popupChild = this.DropDownPopup.Child as FrameworkElement; - var delta = monitorRight - this.PointToScreen(new Point()).X - popupChild.ActualWidth - e.HorizontalChange; - var deltaX = popupChild.ActualWidth - this.galleryPanel.ActualWidth; - var deltaBorders = this.dropDownBorder.ActualWidth - this.galleryPanel.ActualWidth; - - if (delta > 0) - { - this.galleryPanel.Width = Math.Max(0, Math.Max(this.galleryPanel.Width + e.HorizontalChange, this.ActualWidth - deltaBorders)); - } - else - { - this.galleryPanel.Width = Math.Max(0, Math.Max(monitorRight - this.PointToScreen(new Point()).X - deltaX, this.ActualWidth - deltaBorders)); - } - } - - // Handles resize vertical drag - private void OnResizeVerticalDelta(object sender, DragDeltaEventArgs e) - { - this.SetDragHeight(e); - } - - private void SetDragHeight(DragDeltaEventArgs e) - { - if (!this.canSizeY) - { - return; - } - - if (double.IsNaN(this.scrollViewer.Height)) - { - this.scrollViewer.Height = this.scrollViewer.ActualHeight; - } - - if (this.ShowPopupOnTop) - { - var monitorTop = RibbonControl.GetControlMonitor(this).Top; - - // Calc shadow height - var delta = this.PointToScreen(new Point()).Y - this.dropDownBorder.ActualHeight - e.VerticalChange - monitorTop; - if (delta > 0) - { - this.scrollViewer.Height = Math.Max(0, - Math.Min(Math.Max(this.galleryPanel.GetItemSize().Height, this.scrollViewer.Height + e.VerticalChange), this.MaxDropDownHeight)); - } - else - { - delta = this.PointToScreen(new Point()).Y - this.dropDownBorder.ActualHeight - monitorTop; - this.scrollViewer.Height = Math.Max(0, - Math.Min(Math.Max(this.galleryPanel.GetItemSize().Height, this.scrollViewer.Height + delta), this.MaxDropDownHeight)); - } - } - else - { - var monitorBottom = RibbonControl.GetControlMonitor(this).Bottom; - var popupChild = this.DropDownPopup.Child as FrameworkElement; - var delta = monitorBottom - this.PointToScreen(new Point()).Y - this.ActualHeight - popupChild.ActualHeight - e.VerticalChange; - if (delta > 0) - { - this.scrollViewer.Height = Math.Max(0, - Math.Min(Math.Max(this.galleryPanel.GetItemSize().Height, this.scrollViewer.Height + e.VerticalChange), this.MaxDropDownHeight)); - } - else - { - delta = monitorBottom - this.PointToScreen(new Point()).Y - this.ActualHeight - popupChild.ActualHeight; - this.scrollViewer.Height = Math.Max(0, - Math.Min(Math.Max(this.galleryPanel.GetItemSize().Height, this.scrollViewer.Height + delta), this.MaxDropDownHeight)); - } - } - } - - #endregion - } +namespace Fluent +{ + using System; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Threading; + using System.Windows; + using System.Windows.Controls; + using System.Windows.Controls.Primitives; + using System.Windows.Data; + using System.Windows.Input; + using System.Windows.Media; + using System.Windows.Media.Imaging; + using System.Windows.Threading; + using Fluent.Internal; + + /// + /// Represents custom Fluent UI ComboBox + /// + [TemplatePart(Name = "PART_ResizeBothThumb", Type = typeof(Thumb))] + [TemplatePart(Name = "PART_ResizeVerticalThumb", Type = typeof(Thumb))] + public class ComboBox : System.Windows.Controls.ComboBox, IQuickAccessItemProvider, IRibbonControl, IDropDownControl + { + #region Fields + + // Thumb to resize in both directions + private Thumb resizeBothThumb; + // Thumb to resize vertical + private Thumb resizeVerticalThumb; + + private IInputElement focusedElement; + + private Panel menuPanel; + + private Border dropDownBorder; + private Border contentBorder; + + private ContentPresenter contentSite; + + // Freezed image (created during snapping) + private Image snappedImage; + + // Is visual currently snapped + private bool isSnapped; + + private GalleryPanel galleryPanel; + + private ScrollViewer scrollViewer; + + private bool canSizeY; + + #endregion + + #region Properties + + #region Size + + /// + /// Gets or sets Size for the element. + /// + public RibbonControlSize Size + { + get { return (RibbonControlSize)this.GetValue(SizeProperty); } + set { this.SetValue(SizeProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Size. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty SizeProperty = RibbonProperties.SizeProperty.AddOwner(typeof(ComboBox)); + + #endregion + + #region SizeDefinition + + /// + /// Gets or sets SizeDefinition for element. + /// + public RibbonControlSizeDefinition SizeDefinition + { + get { return (RibbonControlSizeDefinition)this.GetValue(SizeDefinitionProperty); } + set { this.SetValue(SizeDefinitionProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for SizeDefinition. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty SizeDefinitionProperty = RibbonProperties.SizeDefinitionProperty.AddOwner(typeof(ComboBox)); + + #endregion + + #region KeyTip + + /// + /// Gets or sets KeyTip for element. + /// + public string KeyTip + { + get { return (string)this.GetValue(KeyTipProperty); } + set { this.SetValue(KeyTipProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Keys. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty KeyTipProperty = Fluent.KeyTip.KeysProperty.AddOwner(typeof(ComboBox)); + + #endregion + + /// + /// Gets drop down popup + /// + public Popup DropDownPopup { get; private set; } + + /// + /// Gets a value indicating whether context menu is opened + /// + public bool IsContextMenuOpened { get; set; } + + #region Header + + /// + /// Gets or sets element Text + /// + public object Header + { + get { return (string)this.GetValue(HeaderProperty); } + set { this.SetValue(HeaderProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Header. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty HeaderProperty = RibbonControl.HeaderProperty.AddOwner(typeof(ComboBox)); + + #endregion + + #region Icon + + /// + /// Gets or sets Icon for the element + /// + public object Icon + { + get { return (ImageSource)this.GetValue(IconProperty); } + set { this.SetValue(IconProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Icon. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IconProperty = RibbonControl.IconProperty.AddOwner(typeof(ComboBox), new UIPropertyMetadata(null, OnIconChanged)); + + private static void OnIconChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var element = d as ComboBox; + var oldElement = e.OldValue as FrameworkElement; + if (oldElement != null) element.RemoveLogicalChild(oldElement); + var newElement = e.NewValue as FrameworkElement; + if (newElement != null) element.AddLogicalChild(newElement); + } + + #endregion + + #region Menu + + /// + /// Gets or sets menu to show in combo box bottom + /// + public RibbonMenu Menu + { + get { return (RibbonMenu)this.GetValue(MenuProperty); } + set { this.SetValue(MenuProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Menu. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty MenuProperty = + DependencyProperty.Register("Menu", typeof(RibbonMenu), typeof(ComboBox), new UIPropertyMetadata(null)); + + #endregion + + #region InputWidth + + /// + /// Gets or sets width of the value input part of combobox + /// + public double InputWidth + { + get { return (double)this.GetValue(InputWidthProperty); } + set { this.SetValue(InputWidthProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for InputWidth. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty InputWidthProperty = + DependencyProperty.Register("InputWidth", typeof(double), typeof(ComboBox), new UIPropertyMetadata(double.NaN)); + + #endregion + + #region ItemHeight + + /// + /// Gets or sets items height + /// + public double ItemHeight + { + get { return (double)this.GetValue(ItemHeightProperty); } + set { this.SetValue(ItemHeightProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for ItemHeight. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty ItemHeightProperty = + DependencyProperty.Register("ItemHeight", typeof(double), typeof(ComboBox), new UIPropertyMetadata(double.NaN)); + + #endregion + + #region GroupBy + + /// + /// Gets or sets name of property which + /// will use to group items in the ComboBox. + /// + public string GroupBy + { + get { return (string)this.GetValue(GroupByProperty); } + set { this.SetValue(GroupByProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for GroupBy. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty GroupByProperty = + DependencyProperty.Register("GroupBy", typeof(string), + typeof(ComboBox), new UIPropertyMetadata(null)); + + #endregion + + #region ResizeMode + + /// + /// Gets or sets context menu resize mode + /// + public ContextMenuResizeMode ResizeMode + { + get { return (ContextMenuResizeMode)this.GetValue(ResizeModeProperty); } + set { this.SetValue(ResizeModeProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for ResizeMode. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty ResizeModeProperty = + DependencyProperty.Register("ResizeMode", typeof(ContextMenuResizeMode), typeof(ComboBox), new UIPropertyMetadata(ContextMenuResizeMode.None)); + + #endregion + + #region Snapping + + /// + /// Snaps / Unsnaps the Visual + /// (remove visuals and substitute with freezed image) + /// + private bool IsSnapped + { + get { return this.isSnapped; } + set + { + if (value == this.isSnapped) return; + if (this.snappedImage == null) return; + if ((value) && (((int)this.contentSite.ActualWidth > 0) && ((int)this.contentSite.ActualHeight > 0))) + { + // Render the freezed image + RenderOptions.SetBitmapScalingMode(this.snappedImage, BitmapScalingMode.NearestNeighbor); + var renderTargetBitmap = new RenderTargetBitmap((int)this.contentSite.ActualWidth + (int)this.contentSite.Margin.Left + (int)this.contentSite.Margin.Right, + (int)this.contentSite.ActualHeight + (int)this.contentSite.Margin.Top + (int)this.contentSite.Margin.Bottom, 96, 96, + PixelFormats.Pbgra32); + renderTargetBitmap.Render(this.contentSite); + this.snappedImage.Source = renderTargetBitmap; + this.snappedImage.FlowDirection = this.FlowDirection; + /*snappedImage.Width = contentSite.ActualWidth; + snappedImage.Height = contentSite.ActualHeight;*/ + this.snappedImage.Visibility = Visibility.Visible; + this.contentSite.Visibility = Visibility.Hidden; + this.isSnapped = value; + } + else + { + this.snappedImage.Visibility = Visibility.Collapsed; + this.contentSite.Visibility = Visibility.Visible; + this.isSnapped = value; + } + + this.InvalidateVisual(); + } + } + + #endregion + + #region DropDownHeight + + /// + /// Gets or sets initial dropdown height + /// + public double DropDownHeight + { + get { return (double)this.GetValue(DropDownHeightProperty); } + set { this.SetValue(DropDownHeightProperty, value); } + } + + /// + /// /Using a DependencyProperty as the backing store for DropDownHeight. This enables animation, styling, binding, + /// etc... + /// + public static readonly DependencyProperty DropDownHeightProperty = + DependencyProperty.Register("InitialDropDownHeight", typeof(double), typeof(ComboBox), new UIPropertyMetadata(double.NaN)); + + #endregion + + #region ShowPopupOnTop + + /// + /// Gets a value indicating whether popup is shown on top; + /// + public bool ShowPopupOnTop + { + get { return (bool)this.GetValue(ShowPopupOnTopProperty); } + private set { this.SetValue(ShowPopupOnTopPropertyKey, value); } + } + + // + private static readonly DependencyPropertyKey ShowPopupOnTopPropertyKey = DependencyProperty.RegisterReadOnly("ShowPopupOnTop", typeof(bool), typeof(ComboBox), new UIPropertyMetadata(false)); + + /// + /// Using a DependencyProperty as the backing store for ShowPopupOnTop. This enables animation, styling, binding, + /// etc... + /// + public static readonly DependencyProperty ShowPopupOnTopProperty = ShowPopupOnTopPropertyKey.DependencyProperty; + + #endregion + + #endregion + + #region Constructors + + /// + /// Static constructor + /// + [SuppressMessage("Microsoft.Performance", "CA1810")] + static ComboBox() + { + var type = typeof(ComboBox); + ToolTipService.Attach(type); + PopupService.Attach(type); + ContextMenuService.Attach(type); + DefaultStyleKeyProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(type)); + SelectedItemProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(OnSelectionItemChanged, CoerceSelectedItem)); + } + + private static void OnSelectionItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var combo = d as ComboBox; + if (!combo.isQuickAccessOpened && !combo.isQuickAccessFocused && (combo.quickAccessCombo != null)) combo.UpdateQuickAccessCombo(); + } + + private static object CoerceSelectedItem(DependencyObject d, object basevalue) + { + var combo = d as ComboBox; + if (combo.isQuickAccessOpened || combo.isQuickAccessFocused) return combo.selectedItem; + return basevalue; + } + + /// + /// Default Constructor + /// + public ComboBox() + { + ContextMenuService.Coerce(this); + } + + #endregion + + #region QuickAccess + + /// + /// Gets control which represents shortcut item. + /// This item MUST be syncronized with the original + /// and send command to original one control. + /// + /// Control which represents shortcut item + public virtual FrameworkElement CreateQuickAccessItem() + { + var combo = new ComboBox(); + RibbonControl.BindQuickAccessItem(this, combo); + RibbonControl.Bind(this, combo, "GroupBy", GroupByProperty, BindingMode.OneWay); + RibbonControl.Bind(this, combo, "ActualWidth", WidthProperty, BindingMode.OneWay); + RibbonControl.Bind(this, combo, "InputWidth", InputWidthProperty, BindingMode.OneWay); + RibbonControl.Bind(this, combo, "ItemHeight", ItemHeightProperty, BindingMode.OneWay); + RibbonControl.Bind(this, combo, "IsEditable", IsEditableProperty, BindingMode.OneWay); + RibbonControl.Bind(this, combo, "IsReadOnly", IsReadOnlyProperty, BindingMode.OneWay); + RibbonControl.Bind(this, combo, "ResizeMode", ResizeModeProperty, BindingMode.OneWay); + RibbonControl.Bind(this, combo, "Text", TextProperty, BindingMode.TwoWay); + + RibbonControl.Bind(this, combo, "DisplayMemberPath", DisplayMemberPathProperty, BindingMode.OneWay); + RibbonControl.Bind(this, combo, "GroupStyleSelector", GroupStyleSelectorProperty, BindingMode.OneWay); + RibbonControl.Bind(this, combo, "ItemContainerStyle", ItemContainerStyleProperty, BindingMode.OneWay); + RibbonControl.Bind(this, combo, "ItemsPanel", ItemsPanelProperty, BindingMode.OneWay); + RibbonControl.Bind(this, combo, "ItemStringFormat", ItemStringFormatProperty, BindingMode.OneWay); + RibbonControl.Bind(this, combo, "ItemTemplate", ItemTemplateProperty, BindingMode.OneWay); + RibbonControl.Bind(this, combo, "SelectedValuePath", SelectedValuePathProperty, BindingMode.OneWay); + RibbonControl.Bind(this, combo, "MaxDropDownHeight", MaxDropDownHeightProperty, BindingMode.OneWay); + combo.DropDownOpened += this.OnQuickAccessOpened; + if (this.IsEditable) combo.GotFocus += this.OnQuickAccessTextBoxGetFocus; + this.quickAccessCombo = combo; + this.UpdateQuickAccessCombo(); + return combo; + } + + private void OnQuickAccessTextBoxGetFocus(object sender, RoutedEventArgs e) + { + this.isQuickAccessFocused = true; + if (!this.isQuickAccessOpened) this.Freeze(); + this.quickAccessCombo.LostFocus += this.OnQuickAccessTextBoxLostFocus; + } + + private void OnQuickAccessTextBoxLostFocus(object sender, RoutedEventArgs e) + { + this.quickAccessCombo.LostFocus -= this.OnQuickAccessTextBoxLostFocus; + if (!this.isQuickAccessOpened) this.Unfreeze(); + this.isQuickAccessFocused = false; + } + + private bool isQuickAccessFocused; + private bool isQuickAccessOpened; + private object selectedItem; + private ComboBox quickAccessCombo; + + private void OnQuickAccessOpened(object sender, EventArgs e) + { + this.isQuickAccessOpened = true; + this.quickAccessCombo.DropDownClosed += this.OnQuickAccessMenuClosed; + this.quickAccessCombo.UpdateLayout(); + if (!this.isQuickAccessFocused) + this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, ((ThreadStart)(() => + { + this.Freeze(); + this.Dispatcher.BeginInvoke(DispatcherPriority.Input, ((ThreadStart)(() => { if (this.quickAccessCombo.SelectedItem != null) (this.quickAccessCombo.ItemContainerGenerator.ContainerFromItem(this.quickAccessCombo.SelectedItem) as ComboBoxItem).BringIntoView(); } + ))); + } + ))); + } + + private void OnQuickAccessMenuClosed(object sender, EventArgs e) + { + this.quickAccessCombo.DropDownClosed -= this.OnQuickAccessMenuClosed; + if (!this.isQuickAccessFocused) this.Unfreeze(); + this.isQuickAccessOpened = false; + } + + private void Freeze() + { + this.IsSnapped = true; + this.selectedItem = this.SelectedItem; + + ItemsControlHelper.MoveItemsToDifferentControl(this, this.quickAccessCombo); + + this.SelectedItem = null; + this.quickAccessCombo.SelectedItem = this.selectedItem; + this.quickAccessCombo.Menu = this.Menu; + this.Menu = null; + this.quickAccessCombo.IsSnapped = false; + } + + private void Unfreeze() + { + var text = this.quickAccessCombo.Text; + this.selectedItem = this.quickAccessCombo.SelectedItem; + this.quickAccessCombo.IsSnapped = true; + + ItemsControlHelper.MoveItemsToDifferentControl(this.quickAccessCombo, this); + + this.quickAccessCombo.SelectedItem = null; + this.SelectedItem = this.selectedItem; + this.Menu = this.quickAccessCombo.Menu; + this.quickAccessCombo.Menu = null; + this.IsSnapped = false; + this.Text = text; + this.UpdateLayout(); + } + + private void UpdateQuickAccessCombo() + { + if (this.IsLoaded == false) + { + this.Loaded += this.OnFirstLoaded; + } + + if (this.IsEditable == false) + { + this.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, (ThreadStart)(() => + { + this.quickAccessCombo.IsSnapped = true; + this.IsSnapped = true; + if (this.snappedImage != null && + this.quickAccessCombo.snappedImage != null) + { + this.quickAccessCombo.snappedImage.Source = this.snappedImage.Source; + this.quickAccessCombo.snappedImage.Visibility = Visibility.Visible; + if (this.quickAccessCombo.IsSnapped == false) + { + this.quickAccessCombo.isSnapped = true; + } + } + this.IsSnapped = false; + })); + } + } + + private void OnFirstLoaded(object sender, RoutedEventArgs e) + { + this.Loaded -= this.OnFirstLoaded; + this.UpdateQuickAccessCombo(); + } + + /// + /// Gets or sets whether control can be added to quick access toolbar + /// + public bool CanAddToQuickAccessToolBar + { + get { return (bool)this.GetValue(CanAddToQuickAccessToolBarProperty); } + set { this.SetValue(CanAddToQuickAccessToolBarProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for CanAddToQuickAccessToolBar. This enables animation, styling, + /// binding, etc... + /// + public static readonly DependencyProperty CanAddToQuickAccessToolBarProperty = RibbonControl.CanAddToQuickAccessToolBarProperty.AddOwner(typeof(ComboBox), new UIPropertyMetadata(true, RibbonControl.OnCanAddToQuickAccessToolbarChanged)); + + #endregion + + #region Overrides + + /// + /// When overridden in a derived class, is invoked whenever application code or internal processes call + /// . + /// + public override void OnApplyTemplate() + { + this.DropDownPopup = this.GetTemplateChild("PART_Popup") as Popup; + + if (this.resizeVerticalThumb != null) + { + this.resizeVerticalThumb.DragDelta -= this.OnResizeVerticalDelta; + } + this.resizeVerticalThumb = this.GetTemplateChild("PART_ResizeVerticalThumb") as Thumb; + if (this.resizeVerticalThumb != null) + { + this.resizeVerticalThumb.DragDelta += this.OnResizeVerticalDelta; + } + + if (this.resizeBothThumb != null) + { + this.resizeBothThumb.DragDelta -= this.OnResizeBothDelta; + } + this.resizeBothThumb = this.GetTemplateChild("PART_ResizeBothThumb") as Thumb; + if (this.resizeBothThumb != null) + { + this.resizeBothThumb.DragDelta += this.OnResizeBothDelta; + } + + this.menuPanel = this.GetTemplateChild("PART_MenuPanel") as Panel; + + this.snappedImage = this.GetTemplateChild("PART_SelectedImage") as Image; + this.contentSite = this.GetTemplateChild("PART_ContentSite") as ContentPresenter; + + if (this.contentBorder != null) this.contentBorder.PreviewMouseDown -= this.OnContentBorderPreviewMouseDown; + this.contentBorder = this.GetTemplateChild("PART_ContentBorder") as Border; + if (this.contentBorder != null) this.contentBorder.PreviewMouseDown += this.OnContentBorderPreviewMouseDown; + + this.galleryPanel = this.GetTemplateChild("PART_GalleryPanel") as GalleryPanel; + this.scrollViewer = this.GetTemplateChild("PART_ScrollViewer") as ScrollViewer; + + this.dropDownBorder = this.GetTemplateChild("PART_DropDownBorder") as Border; + + base.OnApplyTemplate(); + } + + /// + /// Reports when a combo box's popup opens. + /// + /// The event data for the event. + protected override void OnDropDownOpened(EventArgs e) + { + base.OnDropDownOpened(e); + + Mouse.Capture(this, CaptureMode.SubTree); + + if (this.SelectedItem != null) + { + Keyboard.Focus(this.ItemContainerGenerator.ContainerFromItem(this.SelectedItem) as IInputElement); + } + + this.focusedElement = Keyboard.FocusedElement; + + if (this.focusedElement != null) + { + this.focusedElement.LostKeyboardFocus += this.OnFocusedElementLostKeyboardFocus; + } + + this.canSizeY = true; + + this.galleryPanel.Width = double.NaN; + this.scrollViewer.Height = double.NaN; + + var popupChild = this.DropDownPopup.Child as FrameworkElement; + var heightDelta = popupChild.DesiredSize.Height - this.scrollViewer.DesiredSize.Height; + + var initialHeight = Math.Min(RibbonControl.GetControlWorkArea(this).Height * 2 / 3, this.MaxDropDownHeight); + + if (double.IsNaN(this.DropDownHeight) == false) + { + initialHeight = Math.Min(this.DropDownHeight, this.MaxDropDownHeight); + } + + if (this.scrollViewer.DesiredSize.Height > initialHeight) + { + this.scrollViewer.Height = initialHeight; + } + else + { + initialHeight = this.scrollViewer.DesiredSize.Height; + } + + var monitor = RibbonControl.GetControlMonitor(this); + var delta = monitor.Bottom - this.PointToScreen(new Point()).Y - this.ActualHeight - initialHeight - heightDelta; + + if (delta >= 0) + { + this.ShowPopupOnTop = false; + } + else + { + var deltaTop = this.PointToScreen(new Point()).Y - initialHeight - heightDelta - monitor.Top; + + if (deltaTop > delta) + { + this.ShowPopupOnTop = true; + } + else + { + this.ShowPopupOnTop = false; + } + + if (deltaTop < 0) + { + delta = Math.Max(Math.Abs(delta), Math.Abs(deltaTop)); + + if (delta > this.galleryPanel.GetItemSize().Height) + { + this.scrollViewer.Height = delta; + } + else + { + this.canSizeY = false; + this.scrollViewer.Height = this.galleryPanel.GetItemSize().Height; + } + } + } + + popupChild.UpdateLayout(); + } + + /// + /// Reports when a combo box's popup closes. + /// + /// The event data for the event. + protected override void OnDropDownClosed(EventArgs e) + { + base.OnDropDownClosed(e); + if (Mouse.Captured == this) Mouse.Capture(null); + if (this.focusedElement != null) this.focusedElement.LostKeyboardFocus -= this.OnFocusedElementLostKeyboardFocus; + this.focusedElement = null; + this.ShowPopupOnTop = false; + this.galleryPanel.Width = double.NaN; + this.scrollViewer.Height = double.NaN; + } + + private void OnFocusedElementLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e) + { + if (this.focusedElement != null) this.focusedElement.LostKeyboardFocus -= this.OnFocusedElementLostKeyboardFocus; + this.focusedElement = Keyboard.FocusedElement; + if (this.focusedElement != null) + { + this.focusedElement.LostKeyboardFocus += this.OnFocusedElementLostKeyboardFocus; + if ((this.IsEditable) && + (this.Items.Contains(this.ItemContainerGenerator.ItemFromContainer(Keyboard.FocusedElement as DependencyObject)))) + { + this.SelectedItem = this.ItemContainerGenerator.ItemFromContainer(Keyboard.FocusedElement as DependencyObject); + } + } + } + + /// + /// Invoked when a attached routed event occurs. + /// + /// Event data. + protected override void OnPreviewKeyDown(KeyEventArgs e) + { + if ((this.IsEditable) && ((e.Key == Key.Down) || (e.Key == Key.Up)) && (!this.IsDropDownOpen)) + { + this.IsDropDownOpen = true; + e.Handled = true; + return; + } + + base.OnPreviewKeyDown(e); + } + + /// + /// Invoked when a attached routed event occurs. + /// + /// Event data. + protected override void OnKeyDown(KeyEventArgs e) + { + if (e.Key == Key.Down) + { + Debug.WriteLine("Down pressed. FocusedElement - " + Keyboard.FocusedElement); + if ((this.Menu != null) && this.Menu.Items.Contains(this.Menu.ItemContainerGenerator.ItemFromContainer(Keyboard.FocusedElement as DependencyObject))) + { + var indexOfMSelectedItem = this.Menu.ItemContainerGenerator.IndexFromContainer(Keyboard.FocusedElement as DependencyObject); + if (indexOfMSelectedItem != this.Menu.Items.Count - 1) + { + Keyboard.Focus(this.Menu.ItemContainerGenerator.ContainerFromIndex(indexOfMSelectedItem + 1) as IInputElement); + } + else + { + if ((this.Items.Count > 0) && (!this.IsEditable)) + { + Keyboard.Focus(this.ItemContainerGenerator.ContainerFromIndex(0) as IInputElement); + } + else Keyboard.Focus(this.Menu.Items[0] as IInputElement); + } + } + else if (this.Items.Contains(this.ItemContainerGenerator.ItemFromContainer(Keyboard.FocusedElement as DependencyObject))) + { + var indexOfSelectedItem = this.ItemContainerGenerator.IndexFromContainer(Keyboard.FocusedElement as DependencyObject); + if (indexOfSelectedItem != this.Items.Count - 1) + { + Keyboard.Focus(this.ItemContainerGenerator.ContainerFromIndex(indexOfSelectedItem + 1) as IInputElement); + } + else + { + if ((this.Menu != null) && (this.Menu.Items.Count > 0) && (!this.IsEditable)) Keyboard.Focus(this.Menu.ItemContainerGenerator.ContainerFromIndex(0) as IInputElement); + else + { + Keyboard.Focus(this.ItemContainerGenerator.ContainerFromIndex(0) as IInputElement); + } + } + } + else if (this.SelectedItem != null) Keyboard.Focus(this.ItemContainerGenerator.ContainerFromItem(this.SelectedItem) as IInputElement); + e.Handled = true; + Debug.WriteLine("FocusedElement - " + Keyboard.FocusedElement); + return; + } + else if (e.Key == Key.Up) + { + Debug.WriteLine("Up pressed. FocusedElement - " + Keyboard.FocusedElement); + if ((this.Menu != null) && this.Menu.Items.Contains(this.Menu.ItemContainerGenerator.ItemFromContainer(Keyboard.FocusedElement as DependencyObject))) + { + var indexOfMSelectedItem = this.Menu.ItemContainerGenerator.IndexFromContainer(Keyboard.FocusedElement as DependencyObject); + if (indexOfMSelectedItem != 0) + { + Keyboard.Focus(this.Menu.ItemContainerGenerator.ContainerFromIndex(indexOfMSelectedItem - 1) as IInputElement); + } + else + { + if ((this.Items.Count > 0) && (!this.IsEditable)) + { + Keyboard.Focus(this.ItemContainerGenerator.ContainerFromIndex(this.Items.Count - 1) as IInputElement); + } + else Keyboard.Focus(this.Menu.Items[this.Menu.Items.Count - 1] as IInputElement); + } + } + else if (this.Items.Contains(this.ItemContainerGenerator.ItemFromContainer(Keyboard.FocusedElement as DependencyObject))) + { + var indexOfSelectedItem = this.ItemContainerGenerator.IndexFromContainer(Keyboard.FocusedElement as DependencyObject); + if (indexOfSelectedItem != 0) + { + Keyboard.Focus(this.ItemContainerGenerator.ContainerFromIndex(indexOfSelectedItem - 1) as IInputElement); + } + else + { + if ((this.Menu != null) && (this.Menu.Items.Count > 0) && (!this.IsEditable)) Keyboard.Focus(this.Menu.ItemContainerGenerator.ContainerFromIndex(this.Menu.Items.Count - 1) as IInputElement); + else + { + Keyboard.Focus(this.ItemContainerGenerator.ContainerFromIndex(this.Items.Count - 1) as IInputElement); + } + } + } + else if (this.SelectedItem != null) Keyboard.Focus(this.ItemContainerGenerator.ContainerFromItem(this.SelectedItem) as IInputElement); + Debug.WriteLine("FocusedElement - " + Keyboard.FocusedElement); + e.Handled = true; + return; + } + else if ((e.Key == Key.Return) && (!this.IsEditable) && this.IsDropDownOpen) + { + var element = Keyboard.FocusedElement as DependencyObject; + + // only try to select if we got a focusedElement + if (element != null) + { + var newSelectedIndex = this.ItemContainerGenerator.IndexFromContainer(element); + + // only set the selected index if the focused element was in a container in this combobox + if (newSelectedIndex > -1) + { + this.SelectedIndex = newSelectedIndex; + } + } + } + base.OnKeyDown(e); + } + + #endregion + + #region Methods + + /// + /// Handles key tip pressed + /// + public virtual void OnKeyTipPressed() + { + this.Dispatcher.BeginInvoke( + DispatcherPriority.Normal, + (DispatcherOperationCallback)delegate(object arg) + { + var ctrl = (ComboBox)arg; + + // Edge case: Whole dropdown content is disabled + if (ctrl.IsKeyboardFocusWithin == false) + { + Keyboard.Focus(ctrl); + } + return null; + }, + this); + + if (!this.IsEditable) + { + this.IsDropDownOpen = true; + } + } + + /// + /// Handles back navigation with KeyTips + /// + public void OnKeyTipBack() + { + } + + #endregion + + #region Private methods + + // Prevent reopenning of the dropdown menu (popup) + private void OnContentBorderPreviewMouseDown(object sender, MouseButtonEventArgs e) + { + if (this.IsDropDownOpen) + { + this.IsDropDownOpen = false; + e.Handled = true; + } + } + + // Handles resize both drag + private void OnResizeBothDelta(object sender, DragDeltaEventArgs e) + { + // Set height + this.SetDragHeight(e); + + // Set width + this.menuPanel.Width = double.NaN; + if (double.IsNaN(this.galleryPanel.Width)) + { + this.galleryPanel.Width = this.galleryPanel.ActualWidth; + } + + var monitorRight = RibbonControl.GetControlMonitor(this).Right; + var popupChild = this.DropDownPopup.Child as FrameworkElement; + var delta = monitorRight - this.PointToScreen(new Point()).X - popupChild.ActualWidth - e.HorizontalChange; + var deltaX = popupChild.ActualWidth - this.galleryPanel.ActualWidth; + var deltaBorders = this.dropDownBorder.ActualWidth - this.galleryPanel.ActualWidth; + + if (delta > 0) + { + this.galleryPanel.Width = Math.Max(0, Math.Max(this.galleryPanel.Width + e.HorizontalChange, this.ActualWidth - deltaBorders)); + } + else + { + this.galleryPanel.Width = Math.Max(0, Math.Max(monitorRight - this.PointToScreen(new Point()).X - deltaX, this.ActualWidth - deltaBorders)); + } + } + + // Handles resize vertical drag + private void OnResizeVerticalDelta(object sender, DragDeltaEventArgs e) + { + this.SetDragHeight(e); + } + + private void SetDragHeight(DragDeltaEventArgs e) + { + if (!this.canSizeY) + { + return; + } + + if (double.IsNaN(this.scrollViewer.Height)) + { + this.scrollViewer.Height = this.scrollViewer.ActualHeight; + } + + if (this.ShowPopupOnTop) + { + var monitorTop = RibbonControl.GetControlMonitor(this).Top; + + // Calc shadow height + var delta = this.PointToScreen(new Point()).Y - this.dropDownBorder.ActualHeight - e.VerticalChange - monitorTop; + if (delta > 0) + { + this.scrollViewer.Height = Math.Max(0, + Math.Min(Math.Max(this.galleryPanel.GetItemSize().Height, this.scrollViewer.Height + e.VerticalChange), this.MaxDropDownHeight)); + } + else + { + delta = this.PointToScreen(new Point()).Y - this.dropDownBorder.ActualHeight - monitorTop; + this.scrollViewer.Height = Math.Max(0, + Math.Min(Math.Max(this.galleryPanel.GetItemSize().Height, this.scrollViewer.Height + delta), this.MaxDropDownHeight)); + } + } + else + { + var monitorBottom = RibbonControl.GetControlMonitor(this).Bottom; + var popupChild = this.DropDownPopup.Child as FrameworkElement; + var delta = monitorBottom - this.PointToScreen(new Point()).Y - this.ActualHeight - popupChild.ActualHeight - e.VerticalChange; + if (delta > 0) + { + this.scrollViewer.Height = Math.Max(0, + Math.Min(Math.Max(this.galleryPanel.GetItemSize().Height, this.scrollViewer.Height + e.VerticalChange), this.MaxDropDownHeight)); + } + else + { + delta = monitorBottom - this.PointToScreen(new Point()).Y - this.ActualHeight - popupChild.ActualHeight; + this.scrollViewer.Height = Math.Max(0, + Math.Min(Math.Max(this.galleryPanel.GetItemSize().Height, this.scrollViewer.Height + delta), this.MaxDropDownHeight)); + } + } + } + + #endregion + } } \ No newline at end of file diff --git a/Fluent/Controls/ContextMenu.cs b/Fluent.Ribbon/Controls/ContextMenu.cs similarity index 97% rename from Fluent/Controls/ContextMenu.cs rename to Fluent.Ribbon/Controls/ContextMenu.cs index 9daeba239..ae6f9960e 100644 --- a/Fluent/Controls/ContextMenu.cs +++ b/Fluent.Ribbon/Controls/ContextMenu.cs @@ -1,156 +1,156 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.Windows; -using System.Windows.Controls.Primitives; - -namespace Fluent -{ - /// - /// Represents context menu resize mode - /// - public enum ContextMenuResizeMode - { - /// - /// Context menu can not be resized - /// - None = 0, - /// - /// Context menu can be only resized vertically - /// - Vertical, - /// - /// Context menu can be resized vertically and horizontally - /// - Both - } - - /// - /// Represents a pop-up menu that enables a control - /// to expose functionality that is specific to the context of the control - /// - public class ContextMenu : System.Windows.Controls.ContextMenu - { - #region Fields - - // Thumb to resize in both directions - Thumb resizeBothThumb; - // Thumb to resize vertical - Thumb resizeVerticalThumb; - - #endregion - - #region Properties - - /// - /// Gets or sets context menu resize mode - /// - public ContextMenuResizeMode ResizeMode - { - get { return (ContextMenuResizeMode)this.GetValue(ResizeModeProperty); } - set { this.SetValue(ResizeModeProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for ResizeMode. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty ResizeModeProperty = - DependencyProperty.Register("ResizeMode", typeof(ContextMenuResizeMode), - typeof(ContextMenu), new UIPropertyMetadata(ContextMenuResizeMode.None)); - - #endregion - - #region Constructor - - /// - /// Static constructor - /// ] - [SuppressMessage("Microsoft.Performance", "CA1810")] - static ContextMenu() - { - DefaultStyleKeyProperty.OverrideMetadata(typeof(ContextMenu), new FrameworkPropertyMetadata(typeof(ContextMenu))); - FocusVisualStyleProperty.OverrideMetadata(typeof(ContextMenu), new FrameworkPropertyMetadata(null)); - } - - /// - /// Default constructor - /// - public ContextMenu() - { - } - - #endregion - - #region Overrides - - /// - /// When overridden in a derived class, is invoked whenever application code or internal processes call . - /// - public override void OnApplyTemplate() - { - if (this.resizeVerticalThumb != null) - { - this.resizeVerticalThumb.DragDelta -= this.OnResizeVerticalDelta; - } - this.resizeVerticalThumb = this.GetTemplateChild("PART_ResizeVerticalThumb") as Thumb; - if (this.resizeVerticalThumb != null) - { - this.resizeVerticalThumb.DragDelta += this.OnResizeVerticalDelta; - } - - if (this.resizeBothThumb != null) - { - this.resizeBothThumb.DragDelta -= this.OnResizeBothDelta; - } - this.resizeBothThumb = this.GetTemplateChild("PART_ResizeBothThumb") as Thumb; - if (this.resizeBothThumb != null) - { - this.resizeBothThumb.DragDelta += this.OnResizeBothDelta; - } - } - - /// - /// Creates or identifies the element that is used to display the given item. - /// - /// The element that is used to display the given item. - protected override DependencyObject GetContainerForItemOverride() - { - return new MenuItem(); - } - - /// - /// Determines if the specified item is (or is eligible to be) its own container. - /// - /// The item to check. - /// true if the item is (or is eligible to be) its own container; otherwise, false. - protected override bool IsItemItsOwnContainerOverride(object item) - { - return (item is FrameworkElement); - } - - #endregion - - #region Private methods - - // Handles resize both drag - private void OnResizeBothDelta(object sender, DragDeltaEventArgs e) - { - if (double.IsNaN(this.Width)) - this.Width = this.ActualWidth; - if (double.IsNaN(this.Height)) - this.Height = this.ActualHeight; - this.Width = Math.Max(this.MinWidth, this.Width + e.HorizontalChange); - this.Height = Math.Max(this.MinHeight, this.Height + e.VerticalChange); - } - - // Handles resize vertical drag - private void OnResizeVerticalDelta(object sender, DragDeltaEventArgs e) - { - if (double.IsNaN(this.Height)) - this.Height = this.ActualHeight; - this.Height = Math.Max(this.MinHeight, this.Height + e.VerticalChange); - } - - #endregion - } +using System; +using System.Diagnostics.CodeAnalysis; +using System.Windows; +using System.Windows.Controls.Primitives; + +namespace Fluent +{ + /// + /// Represents context menu resize mode + /// + public enum ContextMenuResizeMode + { + /// + /// Context menu can not be resized + /// + None = 0, + /// + /// Context menu can be only resized vertically + /// + Vertical, + /// + /// Context menu can be resized vertically and horizontally + /// + Both + } + + /// + /// Represents a pop-up menu that enables a control + /// to expose functionality that is specific to the context of the control + /// + public class ContextMenu : System.Windows.Controls.ContextMenu + { + #region Fields + + // Thumb to resize in both directions + Thumb resizeBothThumb; + // Thumb to resize vertical + Thumb resizeVerticalThumb; + + #endregion + + #region Properties + + /// + /// Gets or sets context menu resize mode + /// + public ContextMenuResizeMode ResizeMode + { + get { return (ContextMenuResizeMode)this.GetValue(ResizeModeProperty); } + set { this.SetValue(ResizeModeProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for ResizeMode. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty ResizeModeProperty = + DependencyProperty.Register("ResizeMode", typeof(ContextMenuResizeMode), + typeof(ContextMenu), new UIPropertyMetadata(ContextMenuResizeMode.None)); + + #endregion + + #region Constructor + + /// + /// Static constructor + /// ] + [SuppressMessage("Microsoft.Performance", "CA1810")] + static ContextMenu() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(ContextMenu), new FrameworkPropertyMetadata(typeof(ContextMenu))); + FocusVisualStyleProperty.OverrideMetadata(typeof(ContextMenu), new FrameworkPropertyMetadata(null)); + } + + /// + /// Default constructor + /// + public ContextMenu() + { + } + + #endregion + + #region Overrides + + /// + /// When overridden in a derived class, is invoked whenever application code or internal processes call . + /// + public override void OnApplyTemplate() + { + if (this.resizeVerticalThumb != null) + { + this.resizeVerticalThumb.DragDelta -= this.OnResizeVerticalDelta; + } + this.resizeVerticalThumb = this.GetTemplateChild("PART_ResizeVerticalThumb") as Thumb; + if (this.resizeVerticalThumb != null) + { + this.resizeVerticalThumb.DragDelta += this.OnResizeVerticalDelta; + } + + if (this.resizeBothThumb != null) + { + this.resizeBothThumb.DragDelta -= this.OnResizeBothDelta; + } + this.resizeBothThumb = this.GetTemplateChild("PART_ResizeBothThumb") as Thumb; + if (this.resizeBothThumb != null) + { + this.resizeBothThumb.DragDelta += this.OnResizeBothDelta; + } + } + + /// + /// Creates or identifies the element that is used to display the given item. + /// + /// The element that is used to display the given item. + protected override DependencyObject GetContainerForItemOverride() + { + return new MenuItem(); + } + + /// + /// Determines if the specified item is (or is eligible to be) its own container. + /// + /// The item to check. + /// true if the item is (or is eligible to be) its own container; otherwise, false. + protected override bool IsItemItsOwnContainerOverride(object item) + { + return (item is FrameworkElement); + } + + #endregion + + #region Private methods + + // Handles resize both drag + private void OnResizeBothDelta(object sender, DragDeltaEventArgs e) + { + if (double.IsNaN(this.Width)) + this.Width = this.ActualWidth; + if (double.IsNaN(this.Height)) + this.Height = this.ActualHeight; + this.Width = Math.Max(this.MinWidth, this.Width + e.HorizontalChange); + this.Height = Math.Max(this.MinHeight, this.Height + e.VerticalChange); + } + + // Handles resize vertical drag + private void OnResizeVerticalDelta(object sender, DragDeltaEventArgs e) + { + if (double.IsNaN(this.Height)) + this.Height = this.ActualHeight; + this.Height = Math.Max(this.MinHeight, this.Height + e.VerticalChange); + } + + #endregion + } } \ No newline at end of file diff --git a/Fluent/Controls/DropDownButton.cs b/Fluent.Ribbon/Controls/DropDownButton.cs similarity index 97% rename from Fluent/Controls/DropDownButton.cs rename to Fluent.Ribbon/Controls/DropDownButton.cs index 2528ba1d3..9285ddf22 100644 --- a/Fluent/Controls/DropDownButton.cs +++ b/Fluent.Ribbon/Controls/DropDownButton.cs @@ -1,912 +1,912 @@ -namespace Fluent -{ - using System; - using System.Collections; - using System.Diagnostics; - using System.Diagnostics.CodeAnalysis; - using System.Threading; - using System.Windows; - using System.Windows.Controls; - using System.Windows.Controls.Primitives; - using System.Windows.Data; - using System.Windows.Input; - using System.Windows.Markup; - using System.Windows.Threading; - using Fluent.Internal; - - /// - /// Represents drop down button - /// - [ContentProperty("Items")] - [TemplatePart(Name = "PART_ResizeVerticalThumb", Type = typeof(Thumb))] - [TemplatePart(Name = "PART_ResizeBothThumb", Type = typeof(Thumb))] - [TemplatePart(Name = "PART_MenuPanel", Type = typeof(Panel))] - [TemplatePart(Name = "PART_ScrollViewer", Type = typeof(ScrollViewer))] - [TemplatePart(Name = "PART_Popup", Type = typeof(Popup))] - [TemplatePart(Name = "PART_ButtonBorder", Type = typeof(UIElement))] - public class DropDownButton : MenuBase, IQuickAccessItemProvider, IRibbonControl, IDropDownControl - { - #region Fields - - // Thumb to resize in both directions - private Thumb resizeBothThumb; - - // Thumb to resize vertical - private Thumb resizeVerticalThumb; - - private Panel menuPanel; - - private ScrollViewer scrollViewer; - - private UIElement buttonBorder; - - #endregion - - #region Properties - - #region Size - - /// - /// Gets or sets Size for the element. - /// - public RibbonControlSize Size - { - get { return (RibbonControlSize)this.GetValue(SizeProperty); } - set { this.SetValue(SizeProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Size. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty SizeProperty = RibbonProperties.SizeProperty.AddOwner(typeof(DropDownButton)); - - #endregion - - #region SizeDefinition - - /// - /// Gets or sets SizeDefinition for element. - /// - public RibbonControlSizeDefinition SizeDefinition - { - get { return (RibbonControlSizeDefinition)this.GetValue(SizeDefinitionProperty); } - set { this.SetValue(SizeDefinitionProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for SizeDefinition. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty SizeDefinitionProperty = RibbonProperties.SizeDefinitionProperty.AddOwner(typeof(DropDownButton)); - - #endregion - - #region KeyTip - - /// - /// Gets or sets KeyTip for element. - /// - public string KeyTip - { - get { return (string)this.GetValue(KeyTipProperty); } - set { this.SetValue(KeyTipProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Keys. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty KeyTipProperty = Fluent.KeyTip.KeysProperty.AddOwner(typeof(DropDownButton)); - - #endregion - - /// - /// Gets drop down popup - /// - public Popup DropDownPopup { get; private set; } - - /// - /// Gets a value indicating whether context menu is opened - /// - public bool IsContextMenuOpened { get; set; } - - private bool HasCapture - { - get - { - return ReferenceEquals(Mouse.Captured, this); - } - } - - #region Header - - /// - /// Gets or sets element Text - /// - public object Header - { - get { return this.GetValue(HeaderProperty); } - set { this.SetValue(HeaderProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Header. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty HeaderProperty = RibbonControl.HeaderProperty.AddOwner(typeof(DropDownButton)); - - #endregion - - #region Icon - - /// - /// Gets or sets Icon for the element - /// - public object Icon - { - get { return this.GetValue(IconProperty); } - set { this.SetValue(IconProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Icon. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IconProperty = RibbonControl.IconProperty.AddOwner(typeof(DropDownButton), new UIPropertyMetadata(null, OnIconChanged)); - - private static void OnIconChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var element = (DropDownButton)d; - - var oldElement = e.OldValue as FrameworkElement; - - if (oldElement != null) - { - element.RemoveLogicalChild(oldElement); - } - - var newElement = e.NewValue as FrameworkElement; - - if (newElement != null - && LogicalTreeHelper.GetParent(newElement) == null) - { - element.AddLogicalChild(newElement); - } - } - - #endregion - - #region LargeIcon - - /// - /// Gets or sets button large icon - /// - public object LargeIcon - { - get { return this.GetValue(LargeIconProperty); } - set { this.SetValue(LargeIconProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for SmallIcon. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty LargeIconProperty = - DependencyProperty.Register("LargeIcon", typeof(object), - typeof(DropDownButton), new UIPropertyMetadata(null)); - - #endregion - - #region HasTriangle - - /// - /// Gets or sets whether button has triangle - /// - public bool HasTriangle - { - get { return (bool)this.GetValue(HasTriangleProperty); } - set { this.SetValue(HasTriangleProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for HasTriangle. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty HasTriangleProperty = - DependencyProperty.Register( - "HasTriangle", typeof(bool), typeof(DropDownButton), new UIPropertyMetadata(true)); - - #endregion - - #region IsDropDownOpen - - /// - /// Gets or sets whether popup is opened - /// - public bool IsDropDownOpen - { - get { return (bool)this.GetValue(IsDropDownOpenProperty); } - set { this.SetValue(IsDropDownOpenProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for IsOpen. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IsDropDownOpenProperty = - DependencyProperty.Register("IsDropDownOpen", typeof(bool), typeof(DropDownButton), - new UIPropertyMetadata(false, OnIsDropDownOpenChanged)); - - #endregion - - #region ResizeMode - - /// - /// Gets or sets context menu resize mode - /// - public ContextMenuResizeMode ResizeMode - { - get { return (ContextMenuResizeMode)this.GetValue(ResizeModeProperty); } - set { this.SetValue(ResizeModeProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for ResizeMode. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty ResizeModeProperty = - DependencyProperty.Register("ResizeMode", typeof(ContextMenuResizeMode), - typeof(DropDownButton), new UIPropertyMetadata(ContextMenuResizeMode.None)); - - #endregion - - #region MaxDropDownHeight - - /// - /// Get or sets max height of drop down popup - /// - public double MaxDropDownHeight - { - get { return (double)this.GetValue(MaxDropDownHeightProperty); } - set { this.SetValue(MaxDropDownHeightProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for MaxDropDownHeight. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty MaxDropDownHeightProperty = - DependencyProperty.Register("MaxDropDownHeight", typeof(double), typeof(DropDownButton), new UIPropertyMetadata(SystemParameters.PrimaryScreenHeight / 3.0)); - - #endregion - - #region DropDownHeight - - /// - /// Gets or sets initial dropdown height - /// - public double DropDownHeight - { - get { return (double)this.GetValue(DropDownHeightProperty); } - set { this.SetValue(DropDownHeightProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for InitialDropDownHeight. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty DropDownHeightProperty = - DependencyProperty.Register("DropDownHeight", typeof(double), typeof(DropDownButton), new UIPropertyMetadata(double.NaN)); - - #endregion - - #region ClosePopupOnMouseDown - - /// - /// Gets or sets whether the popup of this drop down button should automatically be closed on mouse down. - /// - public bool ClosePopupOnMouseDown - { - get { return (bool)this.GetValue(ClosePopupOnMouseDownProperty); } - set { this.SetValue(ClosePopupOnMouseDownProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for ClosePopupOnMouseDown. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty ClosePopupOnMouseDownProperty = - DependencyProperty.Register("ClosePopupOnMouseDown", typeof(bool), typeof(DropDownButton), new PropertyMetadata(false)); - - #endregion - - #region ClosePopupOnMouseDownDelay - - /// - /// Gets or sets the delay in milliseconds to close the popup on mouse down. - /// - public int ClosePopupOnMouseDownDelay - { - get { return (int)this.GetValue(ClosePopupOnMouseDownDelayProperty); } - set { this.SetValue(ClosePopupOnMouseDownDelayProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for ClosePopupOnMouseDownDelay. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty ClosePopupOnMouseDownDelayProperty = - DependencyProperty.Register("ClosePopupOnMouseDownDelay", typeof(int), typeof(DropDownButton), new PropertyMetadata(150)); - - #endregion - - #endregion - - #region Events - - /// - /// Occurs when context menu is opened - /// - public event EventHandler DropDownOpened; - - /// - /// Occurs when context menu is closed - /// - public event EventHandler DropDownClosed; - - #endregion - - #region Initialize - - /// - /// Static constructor - /// - [SuppressMessage("Microsoft.Performance", "CA1810")] - static DropDownButton() - { - var type = typeof(DropDownButton); - DefaultStyleKeyProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(type)); - - System.Windows.Controls.ToolTipService.IsEnabledProperty.OverrideMetadata(typeof(DropDownButton), new FrameworkPropertyMetadata(null, CoerceToolTipIsEnabled)); - - KeyboardNavigation.ControlTabNavigationProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(KeyboardNavigationMode.Once)); - KeyboardNavigation.DirectionalNavigationProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(KeyboardNavigationMode.Cycle)); - - ToolTipService.Attach(type); - PopupService.Attach(type); - ContextMenuService.Attach(type); - } - - /// - /// Default constructor - /// - public DropDownButton() - { - ContextMenuService.Coerce(this); - - this.Loaded += this.OnLoaded; - this.Unloaded += this.OnUnloaded; - } - - private void OnLoaded(object sender, RoutedEventArgs e) - { - this.SubscribeEvents(); - } - - private void OnUnloaded(object sender, RoutedEventArgs e) - { - this.UnSubscribeEvents(); - } - - private void SubscribeEvents() - { - // Always unsubscribe events to ensure we don't subscribe twice - this.UnSubscribeEvents(); - - if (this.resizeVerticalThumb != null) - { - this.resizeVerticalThumb.DragDelta += this.OnResizeVerticalDelta; - } - - if (this.resizeBothThumb != null) - { - this.resizeBothThumb.DragDelta += this.OnResizeBothDelta; - } - - if (this.buttonBorder != null) - { - this.buttonBorder.MouseLeftButtonDown += this.HandleButtonBorderMouseLeftButtonDown; - } - - if (this.DropDownPopup != null) - { - this.DropDownPopup.KeyDown += this.OnDropDownPopupKeyDown; - this.DropDownPopup.AddHandler(MouseDownEvent, new RoutedEventHandler(this.OnDropDownPopupMouseDown), true); - } - } - - private void UnSubscribeEvents() - { - if (this.resizeVerticalThumb != null) - { - this.resizeVerticalThumb.DragDelta -= this.OnResizeVerticalDelta; - } - - if (this.resizeBothThumb != null) - { - this.resizeBothThumb.DragDelta -= this.OnResizeBothDelta; - } - - if (this.buttonBorder != null) - { - this.buttonBorder.MouseLeftButtonDown -= this.HandleButtonBorderMouseLeftButtonDown; - } - - if (this.DropDownPopup != null) - { - this.DropDownPopup.KeyDown -= this.OnDropDownPopupKeyDown; - this.DropDownPopup.RemoveHandler(MouseDownEvent, new RoutedEventHandler(this.OnDropDownPopupMouseDown)); - } - } - - /// - /// When overridden in a derived class, is invoked whenever application code or internal processes call . - /// - public override void OnApplyTemplate() - { - this.UnSubscribeEvents(); - - this.DropDownPopup = this.Template.FindName("PART_Popup", this) as Popup; - - if (this.DropDownPopup != null) - { - KeyboardNavigation.SetDirectionalNavigation(this.DropDownPopup, KeyboardNavigationMode.Cycle); - KeyboardNavigation.SetTabNavigation(this.DropDownPopup, KeyboardNavigationMode.Continue); - } - - this.resizeVerticalThumb = this.Template.FindName("PART_ResizeVerticalThumb", this) as Thumb; - - this.resizeBothThumb = this.Template.FindName("PART_ResizeBothThumb", this) as Thumb; - - this.menuPanel = this.Template.FindName("PART_MenuPanel", this) as Panel; - - this.scrollViewer = this.Template.FindName("PART_ScrollViewer", this) as ScrollViewer; - - this.buttonBorder = this.Template.FindName("PART_ButtonBorder", this) as UIElement; - - base.OnApplyTemplate(); - - this.SubscribeEvents(); - } - - #endregion - - #region Overrides - - /// - /// Creates or identifies the element that is used to display the given item. - /// - /// The element that is used to display the given item. - protected override DependencyObject GetContainerForItemOverride() - { - return new MenuItem(); - } - - /// - /// Determines if the specified item is (or is eligible to be) its own container. - /// - /// The item to check. - /// - protected override bool IsItemItsOwnContainerOverride(object item) - { - return item is FrameworkElement; - } - - private void OnDropDownPopupKeyDown(object sender, KeyEventArgs e) - { - if (e.Handled) - { - return; - } - - var handled = false; - - switch (e.Key) - { - case Key.Escape: - this.IsDropDownOpen = false; - handled = true; - break; - } - - if (handled) - { - e.Handled = true; - } - } - - private void OnDropDownPopupMouseDown(object sender, RoutedEventArgs e) - { - if (this.ClosePopupOnMouseDown - && this.resizeBothThumb.IsMouseOver == false - && this.resizeVerticalThumb.IsMouseOver == false) - { - e.Handled = false; - - // Note: get outside thread to prevent exceptions (it's a dependency property after all) - var closePopupOnMouseDownDelay = this.ClosePopupOnMouseDownDelay; - - // Ugly workaround, but use a timer to allow routed event to continue - System.Threading.Tasks.Task.Factory.StartNew(() => - { - Thread.Sleep(closePopupOnMouseDownDelay); - - this.Dispatcher.BeginInvoke(new Action(() => this.IsDropDownOpen = false)); - }); - } - } - - private void HandleButtonBorderMouseLeftButtonDown(object sender, MouseButtonEventArgs e) - { - e.Handled = true; - - this.Focus(); - this.IsDropDownOpen = !this.IsDropDownOpen; - } - - /// - /// Provides class handling for the routed event that occurs when the user presses a key. - /// - /// The event data for the event. - protected override void OnKeyDown(KeyEventArgs e) - { - if (e.Handled) - { - return; - } - - var handled = false; - - switch (e.Key) - { - case Key.Down: - if (this.HasItems - && this.IsDropDownOpen == false) // Only handle this for initial navigation. Further navigation is handled by the dropdown itself - { - this.IsDropDownOpen = true; - - var container = this.ItemContainerGenerator.ContainerFromIndex(0); - - NavigateToContainer(container); - - handled = true; - } - break; - - case Key.Up: - if (this.HasItems - && this.IsDropDownOpen == false) // Only handle this for initial navigation. Further navigation is handled by the dropdown itself - { - this.IsDropDownOpen = true; - - var container = this.ItemContainerGenerator.ContainerFromIndex(this.Items.Count - 1); - - NavigateToContainer(container); - - handled = true; - } - break; - - case Key.Escape: - if (this.IsDropDownOpen) - { - this.IsDropDownOpen = false; - handled = true; - } - break; - - case Key.Enter: - case Key.Space: - this.IsDropDownOpen = !this.IsDropDownOpen; - handled = true; - break; - } - - if (handled) - { - e.Handled = true; - } - } - - private static void NavigateToContainer(DependencyObject container) - { - var element = container as FrameworkElement; - - if (element == null) - { - return; - } - - if (element.Focusable) - { - Keyboard.Focus(element); - } - else - { - var predicted = element.PredictFocus(FocusNavigationDirection.Down); - - if (predicted is MenuBase == false) - { - element.MoveFocus(new TraversalRequest(FocusNavigationDirection.Down)); - } - } - } - - private static object CoerceToolTipIsEnabled(DependencyObject d, object basevalue) - { - var control = (DropDownButton)d; - - return !control.IsDropDownOpen; - } - - #endregion - - #region Methods - - /// - /// Handles key tip pressed - /// - public virtual void OnKeyTipPressed() - { - this.IsDropDownOpen = true; - - if (this.DropDownPopup != null - && this.DropDownPopup.Child != null) - { - Keyboard.Focus(this.DropDownPopup.Child); - this.DropDownPopup.Child.MoveFocus(new TraversalRequest(FocusNavigationDirection.First)); - } - } - - /// - /// Handles back navigation with KeyTips - /// - public void OnKeyTipBack() - { - this.IsDropDownOpen = false; - } - - #endregion - - #region Private methods - - // Handles resize both drag - private void OnResizeBothDelta(object sender, DragDeltaEventArgs e) - { - if (this.scrollViewer == null) - { - return; - } - - if (double.IsNaN(this.scrollViewer.Width)) - { - this.scrollViewer.Width = this.scrollViewer.ActualWidth; - } - - if (double.IsNaN(this.scrollViewer.Height)) - { - this.scrollViewer.Height = this.scrollViewer.ActualHeight; - } - - this.scrollViewer.Width = Math.Max(this.ActualWidth, this.scrollViewer.Width + e.HorizontalChange); - this.scrollViewer.Height = Math.Min(Math.Max(this.ActualHeight, this.scrollViewer.Height + e.VerticalChange), this.MaxDropDownHeight); - } - - // Handles resize vertical drag - private void OnResizeVerticalDelta(object sender, DragDeltaEventArgs e) - { - if (this.scrollViewer == null) - { - return; - } - - if (double.IsNaN(this.scrollViewer.Height)) - { - this.scrollViewer.Height = this.scrollViewer.ActualHeight; - } - - this.scrollViewer.Height = Math.Min(Math.Max(this.ActualHeight, this.scrollViewer.Height + e.VerticalChange), this.MaxDropDownHeight); - } - - private static void OnIsDropDownOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var control = (DropDownButton)d; - - var newValue = (bool)e.NewValue; - - control.SetValue(System.Windows.Controls.ToolTipService.IsEnabledProperty, !newValue); - - Debug.WriteLine(string.Format("{0} IsDropDownOpen: {1}", control.Header, newValue)); - - if (newValue) - { - Mouse.Capture(control, CaptureMode.SubTree); - - Keyboard.Focus(control.DropDownPopup); - - control.Dispatcher.BeginInvoke( - DispatcherPriority.Normal, - (DispatcherOperationCallback)delegate(object arg) - { - var ctrl = (DropDownButton)arg; - - var container = ctrl.ItemContainerGenerator.ContainerFromIndex(0); - - NavigateToContainer(container); - - // Edge case: Whole dropdown content is disabled - if (ctrl.IsKeyboardFocusWithin == false) - { - Keyboard.Focus(ctrl.DropDownPopup); - } - - return null; - }, - control); - - control.OnDropDownOpened(); - } - else - { - // If focus is within the subtree, make sure we have the focus so that focus isn't in the disposed hwnd - if (control.IsKeyboardFocusWithin) - { - // make sure the control has focus - control.Focus(); - } - - Mouse.Capture(null); - - control.OnDropDownClosed(); - } - } - - // Handles drop down closed - private void OnDropDownClosed() - { - if (this.DropDownClosed != null) - { - this.DropDownClosed(this, EventArgs.Empty); - } - } - - // Handles drop down opened - private void OnDropDownOpened() - { - if (this.DropDownOpened != null) - { - this.DropDownOpened(this, EventArgs.Empty); - } - } - - #endregion - - #region Quick Access Item Creating - - /// - /// Gets control which represents shortcut item. - /// This item MUST be synchronized with the original - /// and send command to original one control. - /// - /// Control which represents shortcut item - public virtual FrameworkElement CreateQuickAccessItem() - { - var button = new DropDownButton - { - Size = RibbonControlSize.Small - }; - - this.BindQuickAccessItem(button); - RibbonControl.Bind(this, button, "DisplayMemberPath", DisplayMemberPathProperty, BindingMode.OneWay); - RibbonControl.Bind(this, button, "GroupStyleSelector", GroupStyleSelectorProperty, BindingMode.OneWay); - RibbonControl.Bind(this, button, "ItemContainerStyle", ItemContainerStyleProperty, BindingMode.OneWay); - RibbonControl.Bind(this, button, "ItemsPanel", ItemsPanelProperty, BindingMode.OneWay); - RibbonControl.Bind(this, button, "ItemStringFormat", ItemStringFormatProperty, BindingMode.OneWay); - RibbonControl.Bind(this, button, "ItemTemplate", ItemTemplateProperty, BindingMode.OneWay); - - RibbonControl.Bind(this, button, "MaxDropDownHeight", MaxDropDownHeightProperty, BindingMode.OneWay); - - this.BindQuickAccessItemDropDownEvents(button); - - button.DropDownOpened += this.OnQuickAccessOpened; - return button; - } - - /// - /// Handles quick access button drop down menu opened - /// - /// - /// - protected void OnQuickAccessOpened(object sender, EventArgs e) - { - var buttonInQuickAccess = (DropDownButton)sender; - - buttonInQuickAccess.DropDownClosed += this.OnQuickAccessMenuClosedOrUnloaded; - buttonInQuickAccess.Unloaded += this.OnQuickAccessMenuClosedOrUnloaded; - - ItemsControlHelper.MoveItemsToDifferentControl(this, buttonInQuickAccess); - } - - /// - /// Handles quick access button drop down menu closed - /// - /// - /// - protected void OnQuickAccessMenuClosedOrUnloaded(object sender, EventArgs e) - { - var buttonInQuickAccess = (DropDownButton)sender; - buttonInQuickAccess.DropDownClosed -= this.OnQuickAccessMenuClosedOrUnloaded; - buttonInQuickAccess.Unloaded -= this.OnQuickAccessMenuClosedOrUnloaded; - this.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, (Action)(() => - { - ItemsControlHelper.MoveItemsToDifferentControl(buttonInQuickAccess, this); - })); - } - - /// - /// This method must be overridden to bind properties to use in quick access creating - /// - /// Toolbar item - protected virtual void BindQuickAccessItem(FrameworkElement element) - { - RibbonControl.BindQuickAccessItem(this, element); - RibbonControl.Bind(this, element, "ResizeMode", ResizeModeProperty, BindingMode.Default); - RibbonControl.Bind(this, element, "MaxDropDownHeight", MaxDropDownHeightProperty, BindingMode.Default); - RibbonControl.Bind(this, element, "HasTriangle", HasTriangleProperty, BindingMode.Default); - } - - /// - /// Binds the DropDownClosed and DropDownOpened events to the created quick access item - /// - /// Toolbar item - protected void BindQuickAccessItemDropDownEvents(DropDownButton button) - { - if (this.DropDownClosed != null) button.DropDownClosed += this.DropDownClosed; - if (this.DropDownOpened != null) button.DropDownOpened += this.DropDownOpened; - } - - /// - /// Gets or sets whether control can be added to quick access toolbar - /// - public bool CanAddToQuickAccessToolBar - { - get { return (bool)this.GetValue(CanAddToQuickAccessToolBarProperty); } - set { this.SetValue(CanAddToQuickAccessToolBarProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for CanAddToQuickAccessToolBar. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty CanAddToQuickAccessToolBarProperty = RibbonControl.CanAddToQuickAccessToolBarProperty.AddOwner(typeof(DropDownButton), new UIPropertyMetadata(true, RibbonControl.OnCanAddToQuickAccessToolbarChanged)); - - #endregion - - /// - /// Gets an enumerator for the logical child objects of the object. - /// - /// - /// An enumerator for the logical child objects of the object. The default is null. - /// - protected override IEnumerator LogicalChildren - { - get - { - if (this.Icon != null) - { - yield return this.Icon; - } - - foreach (var item in this.Items) - { - yield return item; - } - } - } - } +namespace Fluent +{ + using System; + using System.Collections; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Threading; + using System.Windows; + using System.Windows.Controls; + using System.Windows.Controls.Primitives; + using System.Windows.Data; + using System.Windows.Input; + using System.Windows.Markup; + using System.Windows.Threading; + using Fluent.Internal; + + /// + /// Represents drop down button + /// + [ContentProperty("Items")] + [TemplatePart(Name = "PART_ResizeVerticalThumb", Type = typeof(Thumb))] + [TemplatePart(Name = "PART_ResizeBothThumb", Type = typeof(Thumb))] + [TemplatePart(Name = "PART_MenuPanel", Type = typeof(Panel))] + [TemplatePart(Name = "PART_ScrollViewer", Type = typeof(ScrollViewer))] + [TemplatePart(Name = "PART_Popup", Type = typeof(Popup))] + [TemplatePart(Name = "PART_ButtonBorder", Type = typeof(UIElement))] + public class DropDownButton : MenuBase, IQuickAccessItemProvider, IRibbonControl, IDropDownControl + { + #region Fields + + // Thumb to resize in both directions + private Thumb resizeBothThumb; + + // Thumb to resize vertical + private Thumb resizeVerticalThumb; + + private Panel menuPanel; + + private ScrollViewer scrollViewer; + + private UIElement buttonBorder; + + #endregion + + #region Properties + + #region Size + + /// + /// Gets or sets Size for the element. + /// + public RibbonControlSize Size + { + get { return (RibbonControlSize)this.GetValue(SizeProperty); } + set { this.SetValue(SizeProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Size. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty SizeProperty = RibbonProperties.SizeProperty.AddOwner(typeof(DropDownButton)); + + #endregion + + #region SizeDefinition + + /// + /// Gets or sets SizeDefinition for element. + /// + public RibbonControlSizeDefinition SizeDefinition + { + get { return (RibbonControlSizeDefinition)this.GetValue(SizeDefinitionProperty); } + set { this.SetValue(SizeDefinitionProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for SizeDefinition. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty SizeDefinitionProperty = RibbonProperties.SizeDefinitionProperty.AddOwner(typeof(DropDownButton)); + + #endregion + + #region KeyTip + + /// + /// Gets or sets KeyTip for element. + /// + public string KeyTip + { + get { return (string)this.GetValue(KeyTipProperty); } + set { this.SetValue(KeyTipProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Keys. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty KeyTipProperty = Fluent.KeyTip.KeysProperty.AddOwner(typeof(DropDownButton)); + + #endregion + + /// + /// Gets drop down popup + /// + public Popup DropDownPopup { get; private set; } + + /// + /// Gets a value indicating whether context menu is opened + /// + public bool IsContextMenuOpened { get; set; } + + private bool HasCapture + { + get + { + return ReferenceEquals(Mouse.Captured, this); + } + } + + #region Header + + /// + /// Gets or sets element Text + /// + public object Header + { + get { return this.GetValue(HeaderProperty); } + set { this.SetValue(HeaderProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Header. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty HeaderProperty = RibbonControl.HeaderProperty.AddOwner(typeof(DropDownButton)); + + #endregion + + #region Icon + + /// + /// Gets or sets Icon for the element + /// + public object Icon + { + get { return this.GetValue(IconProperty); } + set { this.SetValue(IconProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Icon. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IconProperty = RibbonControl.IconProperty.AddOwner(typeof(DropDownButton), new UIPropertyMetadata(null, OnIconChanged)); + + private static void OnIconChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var element = (DropDownButton)d; + + var oldElement = e.OldValue as FrameworkElement; + + if (oldElement != null) + { + element.RemoveLogicalChild(oldElement); + } + + var newElement = e.NewValue as FrameworkElement; + + if (newElement != null + && LogicalTreeHelper.GetParent(newElement) == null) + { + element.AddLogicalChild(newElement); + } + } + + #endregion + + #region LargeIcon + + /// + /// Gets or sets button large icon + /// + public object LargeIcon + { + get { return this.GetValue(LargeIconProperty); } + set { this.SetValue(LargeIconProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for SmallIcon. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty LargeIconProperty = + DependencyProperty.Register("LargeIcon", typeof(object), + typeof(DropDownButton), new UIPropertyMetadata(null)); + + #endregion + + #region HasTriangle + + /// + /// Gets or sets whether button has triangle + /// + public bool HasTriangle + { + get { return (bool)this.GetValue(HasTriangleProperty); } + set { this.SetValue(HasTriangleProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for HasTriangle. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty HasTriangleProperty = + DependencyProperty.Register( + "HasTriangle", typeof(bool), typeof(DropDownButton), new UIPropertyMetadata(true)); + + #endregion + + #region IsDropDownOpen + + /// + /// Gets or sets whether popup is opened + /// + public bool IsDropDownOpen + { + get { return (bool)this.GetValue(IsDropDownOpenProperty); } + set { this.SetValue(IsDropDownOpenProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for IsOpen. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IsDropDownOpenProperty = + DependencyProperty.Register("IsDropDownOpen", typeof(bool), typeof(DropDownButton), + new UIPropertyMetadata(false, OnIsDropDownOpenChanged)); + + #endregion + + #region ResizeMode + + /// + /// Gets or sets context menu resize mode + /// + public ContextMenuResizeMode ResizeMode + { + get { return (ContextMenuResizeMode)this.GetValue(ResizeModeProperty); } + set { this.SetValue(ResizeModeProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for ResizeMode. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty ResizeModeProperty = + DependencyProperty.Register("ResizeMode", typeof(ContextMenuResizeMode), + typeof(DropDownButton), new UIPropertyMetadata(ContextMenuResizeMode.None)); + + #endregion + + #region MaxDropDownHeight + + /// + /// Get or sets max height of drop down popup + /// + public double MaxDropDownHeight + { + get { return (double)this.GetValue(MaxDropDownHeightProperty); } + set { this.SetValue(MaxDropDownHeightProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for MaxDropDownHeight. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty MaxDropDownHeightProperty = + DependencyProperty.Register("MaxDropDownHeight", typeof(double), typeof(DropDownButton), new UIPropertyMetadata(SystemParameters.PrimaryScreenHeight / 3.0)); + + #endregion + + #region DropDownHeight + + /// + /// Gets or sets initial dropdown height + /// + public double DropDownHeight + { + get { return (double)this.GetValue(DropDownHeightProperty); } + set { this.SetValue(DropDownHeightProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for InitialDropDownHeight. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty DropDownHeightProperty = + DependencyProperty.Register("DropDownHeight", typeof(double), typeof(DropDownButton), new UIPropertyMetadata(double.NaN)); + + #endregion + + #region ClosePopupOnMouseDown + + /// + /// Gets or sets whether the popup of this drop down button should automatically be closed on mouse down. + /// + public bool ClosePopupOnMouseDown + { + get { return (bool)this.GetValue(ClosePopupOnMouseDownProperty); } + set { this.SetValue(ClosePopupOnMouseDownProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for ClosePopupOnMouseDown. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty ClosePopupOnMouseDownProperty = + DependencyProperty.Register("ClosePopupOnMouseDown", typeof(bool), typeof(DropDownButton), new PropertyMetadata(false)); + + #endregion + + #region ClosePopupOnMouseDownDelay + + /// + /// Gets or sets the delay in milliseconds to close the popup on mouse down. + /// + public int ClosePopupOnMouseDownDelay + { + get { return (int)this.GetValue(ClosePopupOnMouseDownDelayProperty); } + set { this.SetValue(ClosePopupOnMouseDownDelayProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for ClosePopupOnMouseDownDelay. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty ClosePopupOnMouseDownDelayProperty = + DependencyProperty.Register("ClosePopupOnMouseDownDelay", typeof(int), typeof(DropDownButton), new PropertyMetadata(150)); + + #endregion + + #endregion + + #region Events + + /// + /// Occurs when context menu is opened + /// + public event EventHandler DropDownOpened; + + /// + /// Occurs when context menu is closed + /// + public event EventHandler DropDownClosed; + + #endregion + + #region Initialize + + /// + /// Static constructor + /// + [SuppressMessage("Microsoft.Performance", "CA1810")] + static DropDownButton() + { + var type = typeof(DropDownButton); + DefaultStyleKeyProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(type)); + + System.Windows.Controls.ToolTipService.IsEnabledProperty.OverrideMetadata(typeof(DropDownButton), new FrameworkPropertyMetadata(null, CoerceToolTipIsEnabled)); + + KeyboardNavigation.ControlTabNavigationProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(KeyboardNavigationMode.Once)); + KeyboardNavigation.DirectionalNavigationProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(KeyboardNavigationMode.Cycle)); + + ToolTipService.Attach(type); + PopupService.Attach(type); + ContextMenuService.Attach(type); + } + + /// + /// Default constructor + /// + public DropDownButton() + { + ContextMenuService.Coerce(this); + + this.Loaded += this.OnLoaded; + this.Unloaded += this.OnUnloaded; + } + + private void OnLoaded(object sender, RoutedEventArgs e) + { + this.SubscribeEvents(); + } + + private void OnUnloaded(object sender, RoutedEventArgs e) + { + this.UnSubscribeEvents(); + } + + private void SubscribeEvents() + { + // Always unsubscribe events to ensure we don't subscribe twice + this.UnSubscribeEvents(); + + if (this.resizeVerticalThumb != null) + { + this.resizeVerticalThumb.DragDelta += this.OnResizeVerticalDelta; + } + + if (this.resizeBothThumb != null) + { + this.resizeBothThumb.DragDelta += this.OnResizeBothDelta; + } + + if (this.buttonBorder != null) + { + this.buttonBorder.MouseLeftButtonDown += this.HandleButtonBorderMouseLeftButtonDown; + } + + if (this.DropDownPopup != null) + { + this.DropDownPopup.KeyDown += this.OnDropDownPopupKeyDown; + this.DropDownPopup.AddHandler(MouseDownEvent, new RoutedEventHandler(this.OnDropDownPopupMouseDown), true); + } + } + + private void UnSubscribeEvents() + { + if (this.resizeVerticalThumb != null) + { + this.resizeVerticalThumb.DragDelta -= this.OnResizeVerticalDelta; + } + + if (this.resizeBothThumb != null) + { + this.resizeBothThumb.DragDelta -= this.OnResizeBothDelta; + } + + if (this.buttonBorder != null) + { + this.buttonBorder.MouseLeftButtonDown -= this.HandleButtonBorderMouseLeftButtonDown; + } + + if (this.DropDownPopup != null) + { + this.DropDownPopup.KeyDown -= this.OnDropDownPopupKeyDown; + this.DropDownPopup.RemoveHandler(MouseDownEvent, new RoutedEventHandler(this.OnDropDownPopupMouseDown)); + } + } + + /// + /// When overridden in a derived class, is invoked whenever application code or internal processes call . + /// + public override void OnApplyTemplate() + { + this.UnSubscribeEvents(); + + this.DropDownPopup = this.Template.FindName("PART_Popup", this) as Popup; + + if (this.DropDownPopup != null) + { + KeyboardNavigation.SetDirectionalNavigation(this.DropDownPopup, KeyboardNavigationMode.Cycle); + KeyboardNavigation.SetTabNavigation(this.DropDownPopup, KeyboardNavigationMode.Continue); + } + + this.resizeVerticalThumb = this.Template.FindName("PART_ResizeVerticalThumb", this) as Thumb; + + this.resizeBothThumb = this.Template.FindName("PART_ResizeBothThumb", this) as Thumb; + + this.menuPanel = this.Template.FindName("PART_MenuPanel", this) as Panel; + + this.scrollViewer = this.Template.FindName("PART_ScrollViewer", this) as ScrollViewer; + + this.buttonBorder = this.Template.FindName("PART_ButtonBorder", this) as UIElement; + + base.OnApplyTemplate(); + + this.SubscribeEvents(); + } + + #endregion + + #region Overrides + + /// + /// Creates or identifies the element that is used to display the given item. + /// + /// The element that is used to display the given item. + protected override DependencyObject GetContainerForItemOverride() + { + return new MenuItem(); + } + + /// + /// Determines if the specified item is (or is eligible to be) its own container. + /// + /// The item to check. + /// + protected override bool IsItemItsOwnContainerOverride(object item) + { + return item is FrameworkElement; + } + + private void OnDropDownPopupKeyDown(object sender, KeyEventArgs e) + { + if (e.Handled) + { + return; + } + + var handled = false; + + switch (e.Key) + { + case Key.Escape: + this.IsDropDownOpen = false; + handled = true; + break; + } + + if (handled) + { + e.Handled = true; + } + } + + private void OnDropDownPopupMouseDown(object sender, RoutedEventArgs e) + { + if (this.ClosePopupOnMouseDown + && this.resizeBothThumb.IsMouseOver == false + && this.resizeVerticalThumb.IsMouseOver == false) + { + e.Handled = false; + + // Note: get outside thread to prevent exceptions (it's a dependency property after all) + var closePopupOnMouseDownDelay = this.ClosePopupOnMouseDownDelay; + + // Ugly workaround, but use a timer to allow routed event to continue + System.Threading.Tasks.Task.Factory.StartNew(() => + { + Thread.Sleep(closePopupOnMouseDownDelay); + + this.Dispatcher.BeginInvoke(new Action(() => this.IsDropDownOpen = false)); + }); + } + } + + private void HandleButtonBorderMouseLeftButtonDown(object sender, MouseButtonEventArgs e) + { + e.Handled = true; + + this.Focus(); + this.IsDropDownOpen = !this.IsDropDownOpen; + } + + /// + /// Provides class handling for the routed event that occurs when the user presses a key. + /// + /// The event data for the event. + protected override void OnKeyDown(KeyEventArgs e) + { + if (e.Handled) + { + return; + } + + var handled = false; + + switch (e.Key) + { + case Key.Down: + if (this.HasItems + && this.IsDropDownOpen == false) // Only handle this for initial navigation. Further navigation is handled by the dropdown itself + { + this.IsDropDownOpen = true; + + var container = this.ItemContainerGenerator.ContainerFromIndex(0); + + NavigateToContainer(container); + + handled = true; + } + break; + + case Key.Up: + if (this.HasItems + && this.IsDropDownOpen == false) // Only handle this for initial navigation. Further navigation is handled by the dropdown itself + { + this.IsDropDownOpen = true; + + var container = this.ItemContainerGenerator.ContainerFromIndex(this.Items.Count - 1); + + NavigateToContainer(container); + + handled = true; + } + break; + + case Key.Escape: + if (this.IsDropDownOpen) + { + this.IsDropDownOpen = false; + handled = true; + } + break; + + case Key.Enter: + case Key.Space: + this.IsDropDownOpen = !this.IsDropDownOpen; + handled = true; + break; + } + + if (handled) + { + e.Handled = true; + } + } + + private static void NavigateToContainer(DependencyObject container) + { + var element = container as FrameworkElement; + + if (element == null) + { + return; + } + + if (element.Focusable) + { + Keyboard.Focus(element); + } + else + { + var predicted = element.PredictFocus(FocusNavigationDirection.Down); + + if (predicted is MenuBase == false) + { + element.MoveFocus(new TraversalRequest(FocusNavigationDirection.Down)); + } + } + } + + private static object CoerceToolTipIsEnabled(DependencyObject d, object basevalue) + { + var control = (DropDownButton)d; + + return !control.IsDropDownOpen; + } + + #endregion + + #region Methods + + /// + /// Handles key tip pressed + /// + public virtual void OnKeyTipPressed() + { + this.IsDropDownOpen = true; + + if (this.DropDownPopup != null + && this.DropDownPopup.Child != null) + { + Keyboard.Focus(this.DropDownPopup.Child); + this.DropDownPopup.Child.MoveFocus(new TraversalRequest(FocusNavigationDirection.First)); + } + } + + /// + /// Handles back navigation with KeyTips + /// + public void OnKeyTipBack() + { + this.IsDropDownOpen = false; + } + + #endregion + + #region Private methods + + // Handles resize both drag + private void OnResizeBothDelta(object sender, DragDeltaEventArgs e) + { + if (this.scrollViewer == null) + { + return; + } + + if (double.IsNaN(this.scrollViewer.Width)) + { + this.scrollViewer.Width = this.scrollViewer.ActualWidth; + } + + if (double.IsNaN(this.scrollViewer.Height)) + { + this.scrollViewer.Height = this.scrollViewer.ActualHeight; + } + + this.scrollViewer.Width = Math.Max(this.ActualWidth, this.scrollViewer.Width + e.HorizontalChange); + this.scrollViewer.Height = Math.Min(Math.Max(this.ActualHeight, this.scrollViewer.Height + e.VerticalChange), this.MaxDropDownHeight); + } + + // Handles resize vertical drag + private void OnResizeVerticalDelta(object sender, DragDeltaEventArgs e) + { + if (this.scrollViewer == null) + { + return; + } + + if (double.IsNaN(this.scrollViewer.Height)) + { + this.scrollViewer.Height = this.scrollViewer.ActualHeight; + } + + this.scrollViewer.Height = Math.Min(Math.Max(this.ActualHeight, this.scrollViewer.Height + e.VerticalChange), this.MaxDropDownHeight); + } + + private static void OnIsDropDownOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var control = (DropDownButton)d; + + var newValue = (bool)e.NewValue; + + control.SetValue(System.Windows.Controls.ToolTipService.IsEnabledProperty, !newValue); + + Debug.WriteLine(string.Format("{0} IsDropDownOpen: {1}", control.Header, newValue)); + + if (newValue) + { + Mouse.Capture(control, CaptureMode.SubTree); + + Keyboard.Focus(control.DropDownPopup); + + control.Dispatcher.BeginInvoke( + DispatcherPriority.Normal, + (DispatcherOperationCallback)delegate(object arg) + { + var ctrl = (DropDownButton)arg; + + var container = ctrl.ItemContainerGenerator.ContainerFromIndex(0); + + NavigateToContainer(container); + + // Edge case: Whole dropdown content is disabled + if (ctrl.IsKeyboardFocusWithin == false) + { + Keyboard.Focus(ctrl.DropDownPopup); + } + + return null; + }, + control); + + control.OnDropDownOpened(); + } + else + { + // If focus is within the subtree, make sure we have the focus so that focus isn't in the disposed hwnd + if (control.IsKeyboardFocusWithin) + { + // make sure the control has focus + control.Focus(); + } + + Mouse.Capture(null); + + control.OnDropDownClosed(); + } + } + + // Handles drop down closed + private void OnDropDownClosed() + { + if (this.DropDownClosed != null) + { + this.DropDownClosed(this, EventArgs.Empty); + } + } + + // Handles drop down opened + private void OnDropDownOpened() + { + if (this.DropDownOpened != null) + { + this.DropDownOpened(this, EventArgs.Empty); + } + } + + #endregion + + #region Quick Access Item Creating + + /// + /// Gets control which represents shortcut item. + /// This item MUST be synchronized with the original + /// and send command to original one control. + /// + /// Control which represents shortcut item + public virtual FrameworkElement CreateQuickAccessItem() + { + var button = new DropDownButton + { + Size = RibbonControlSize.Small + }; + + this.BindQuickAccessItem(button); + RibbonControl.Bind(this, button, "DisplayMemberPath", DisplayMemberPathProperty, BindingMode.OneWay); + RibbonControl.Bind(this, button, "GroupStyleSelector", GroupStyleSelectorProperty, BindingMode.OneWay); + RibbonControl.Bind(this, button, "ItemContainerStyle", ItemContainerStyleProperty, BindingMode.OneWay); + RibbonControl.Bind(this, button, "ItemsPanel", ItemsPanelProperty, BindingMode.OneWay); + RibbonControl.Bind(this, button, "ItemStringFormat", ItemStringFormatProperty, BindingMode.OneWay); + RibbonControl.Bind(this, button, "ItemTemplate", ItemTemplateProperty, BindingMode.OneWay); + + RibbonControl.Bind(this, button, "MaxDropDownHeight", MaxDropDownHeightProperty, BindingMode.OneWay); + + this.BindQuickAccessItemDropDownEvents(button); + + button.DropDownOpened += this.OnQuickAccessOpened; + return button; + } + + /// + /// Handles quick access button drop down menu opened + /// + /// + /// + protected void OnQuickAccessOpened(object sender, EventArgs e) + { + var buttonInQuickAccess = (DropDownButton)sender; + + buttonInQuickAccess.DropDownClosed += this.OnQuickAccessMenuClosedOrUnloaded; + buttonInQuickAccess.Unloaded += this.OnQuickAccessMenuClosedOrUnloaded; + + ItemsControlHelper.MoveItemsToDifferentControl(this, buttonInQuickAccess); + } + + /// + /// Handles quick access button drop down menu closed + /// + /// + /// + protected void OnQuickAccessMenuClosedOrUnloaded(object sender, EventArgs e) + { + var buttonInQuickAccess = (DropDownButton)sender; + buttonInQuickAccess.DropDownClosed -= this.OnQuickAccessMenuClosedOrUnloaded; + buttonInQuickAccess.Unloaded -= this.OnQuickAccessMenuClosedOrUnloaded; + this.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, (Action)(() => + { + ItemsControlHelper.MoveItemsToDifferentControl(buttonInQuickAccess, this); + })); + } + + /// + /// This method must be overridden to bind properties to use in quick access creating + /// + /// Toolbar item + protected virtual void BindQuickAccessItem(FrameworkElement element) + { + RibbonControl.BindQuickAccessItem(this, element); + RibbonControl.Bind(this, element, "ResizeMode", ResizeModeProperty, BindingMode.Default); + RibbonControl.Bind(this, element, "MaxDropDownHeight", MaxDropDownHeightProperty, BindingMode.Default); + RibbonControl.Bind(this, element, "HasTriangle", HasTriangleProperty, BindingMode.Default); + } + + /// + /// Binds the DropDownClosed and DropDownOpened events to the created quick access item + /// + /// Toolbar item + protected void BindQuickAccessItemDropDownEvents(DropDownButton button) + { + if (this.DropDownClosed != null) button.DropDownClosed += this.DropDownClosed; + if (this.DropDownOpened != null) button.DropDownOpened += this.DropDownOpened; + } + + /// + /// Gets or sets whether control can be added to quick access toolbar + /// + public bool CanAddToQuickAccessToolBar + { + get { return (bool)this.GetValue(CanAddToQuickAccessToolBarProperty); } + set { this.SetValue(CanAddToQuickAccessToolBarProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for CanAddToQuickAccessToolBar. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty CanAddToQuickAccessToolBarProperty = RibbonControl.CanAddToQuickAccessToolBarProperty.AddOwner(typeof(DropDownButton), new UIPropertyMetadata(true, RibbonControl.OnCanAddToQuickAccessToolbarChanged)); + + #endregion + + /// + /// Gets an enumerator for the logical child objects of the object. + /// + /// + /// An enumerator for the logical child objects of the object. The default is null. + /// + protected override IEnumerator LogicalChildren + { + get + { + if (this.Icon != null) + { + yield return this.Icon; + } + + foreach (var item in this.Items) + { + yield return item; + } + } + } + } } \ No newline at end of file diff --git a/Fluent/Controls/Gallery.cs b/Fluent.Ribbon/Controls/Gallery.cs similarity index 97% rename from Fluent/Controls/Gallery.cs rename to Fluent.Ribbon/Controls/Gallery.cs index 8e7df86d3..f34f23ab1 100644 --- a/Fluent/Controls/Gallery.cs +++ b/Fluent.Ribbon/Controls/Gallery.cs @@ -1,533 +1,533 @@ -using System; -using System.Collections.ObjectModel; -using System.Collections.Specialized; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Input; -using System.Windows.Markup; - -namespace Fluent -{ - // TODO: add TemplatePart's in Gallery (!) - - /// - /// Represents gallery control. - /// Usually a gallery is hosted in context menu - /// - [ContentProperty("Items")] - public class Gallery : ListBox - { - #region Fields - - private ObservableCollection filters; - - private DropDownButton groupsMenuButton; - - #endregion - - #region Properties - - #region MinItemsInRow - - /// - /// Min width of the Gallery - /// - public int MinItemsInRow - { - get { return (int)this.GetValue(MinItemsInRowProperty); } - set { this.SetValue(MinItemsInRowProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for MinItemsInRow. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty MinItemsInRowProperty = - DependencyProperty.Register("MinItemsInRow", typeof(int), - typeof(Gallery), new UIPropertyMetadata(1)); - - #endregion - - #region MaxItemsInRow - - /// - /// Max width of the Gallery - /// - public int MaxItemsInRow - { - get { return (int)this.GetValue(MaxItemsInRowProperty); } - set { this.SetValue(MaxItemsInRowProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for MaxItemsInRow. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty MaxItemsInRowProperty = - DependencyProperty.Register("MaxItemsInRow", typeof(int), - typeof(Gallery), new UIPropertyMetadata(int.MaxValue)); - - #endregion - - #region GroupBy - - /// - /// Gets or sets name of property which - /// will use to group items in the Gallery. - /// - public string GroupBy - { - get { return (string)this.GetValue(GroupByProperty); } - set { this.SetValue(GroupByProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for GroupBy. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty GroupByProperty = - DependencyProperty.Register("GroupBy", typeof(string), typeof(Gallery), - new UIPropertyMetadata(null)); - - #endregion - - #region Orientation - - /// - /// Gets or sets orientation of gallery - /// - public Orientation Orientation - { - get { return (Orientation)this.GetValue(OrientationProperty); } - set { this.SetValue(OrientationProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Orientation. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty OrientationProperty = - DependencyProperty.Register("Orientation", typeof(Orientation), - typeof(Gallery), new UIPropertyMetadata(Orientation.Horizontal)); - - #endregion - - #region ItemWidth - - /// - /// Gets or sets item width - /// - public double ItemWidth - { - get { return (double)this.GetValue(ItemWidthProperty); } - set { this.SetValue(ItemWidthProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for ItemWidth. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty ItemWidthProperty = - DependencyProperty.Register("ItemWidth", typeof(double), typeof(Gallery), new UIPropertyMetadata(double.NaN)); - - /// - /// Gets or sets item height - /// - public double ItemHeight - { - get { return (double)this.GetValue(ItemHeightProperty); } - set { this.SetValue(ItemHeightProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for ItemHeight. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty ItemHeightProperty = - DependencyProperty.Register("ItemHeight", typeof(double), typeof(Gallery), new UIPropertyMetadata(double.NaN)); - - #endregion - - #region Filters - - /// - /// Gets collection of filters - /// - public ObservableCollection Filters - { - get - { - if (this.filters == null) - { - this.filters = new ObservableCollection(); - this.filters.CollectionChanged += this.OnFilterCollectionChanged; - } - return this.filters; - } - } - - // Handle toolbar items changes - void OnFilterCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) - { - this.HasFilter = this.Filters.Count > 0; - this.InvalidateProperty(SelectedFilterProperty); - - switch (e.Action) - { - case NotifyCollectionChangedAction.Add: - for (int i = 0; i < e.NewItems.Count; i++) - { - if (this.groupsMenuButton != null) - { - GalleryGroupFilter filter = (GalleryGroupFilter)e.NewItems[i]; - MenuItem menuItem = new MenuItem(); - menuItem.Header = filter.Title; - menuItem.Tag = filter; - if (filter == this.SelectedFilter) menuItem.IsChecked = true; - menuItem.Click += this.OnFilterMenuItemClick; - this.groupsMenuButton.Items.Insert(e.NewStartingIndex + i, menuItem); - } - } - break; - case NotifyCollectionChangedAction.Remove: - foreach (object item in e.OldItems) - { - if (this.groupsMenuButton != null) - { - this.groupsMenuButton.Items.Remove(this.GetFilterMenuItem(item as GalleryGroupFilter)); - } - } - break; - - case NotifyCollectionChangedAction.Replace: - foreach (object item in e.OldItems) - { - if (this.groupsMenuButton != null) - { - this.groupsMenuButton.Items.Remove(this.GetFilterMenuItem(item as GalleryGroupFilter)); - } - } - foreach (var item in e.NewItems.OfType()) - { - if (this.groupsMenuButton != null) - { - GalleryGroupFilter filter = item; - MenuItem menuItem = new MenuItem(); - menuItem.Header = filter.Title; - menuItem.Tag = filter; - if (filter == this.SelectedFilter) menuItem.IsChecked = true; - menuItem.Click += this.OnFilterMenuItemClick; - this.groupsMenuButton.Items.Add(menuItem); - } - } - break; - } - } - - /// - /// Gets or sets selected filter - /// - public GalleryGroupFilter SelectedFilter - { - get { return (GalleryGroupFilter)this.GetValue(SelectedFilterProperty); } - set { this.SetValue(SelectedFilterProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for SelectedFilter. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty SelectedFilterProperty = - DependencyProperty.Register("SelectedFilter", typeof(GalleryGroupFilter), - typeof(Gallery), new UIPropertyMetadata(null, OnFilterChanged, CoerceSelectedFilter)); - - // Coerce selected filter - static object CoerceSelectedFilter(DependencyObject d, object basevalue) - { - Gallery gallery = (Gallery)d; - if ((basevalue == null) && (gallery.Filters.Count > 0)) return gallery.Filters[0]; - return basevalue; - } - - // Handles filter property changed - static void OnFilterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - Gallery gallery = (Gallery)d; - GalleryGroupFilter filter = e.NewValue as GalleryGroupFilter; - if (filter != null) - { - gallery.SelectedFilterTitle = filter.Title; - gallery.SelectedFilterGroups = filter.Groups; - } - else - { - gallery.SelectedFilterTitle = ""; - gallery.SelectedFilterGroups = null; - } - gallery.UpdateLayout(); - } - - /// - /// Gets selected filter title - /// - public string SelectedFilterTitle - { - get { return (string)this.GetValue(SelectedFilterTitleProperty); } - private set { this.SetValue(SelectedFilterTitlePropertyKey, value); } - } - - private static readonly DependencyPropertyKey SelectedFilterTitlePropertyKey = - DependencyProperty.RegisterReadOnly("SelectedFilterTitle", typeof(string), - typeof(Gallery), new UIPropertyMetadata(null)); - - /// - /// Using a DependencyProperty as the backing store for SelectedFilterTitle. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty SelectedFilterTitleProperty = SelectedFilterTitlePropertyKey.DependencyProperty; - - /// - /// Gets selected filter groups - /// - public string SelectedFilterGroups - { - get { return (string)this.GetValue(SelectedFilterGroupsProperty); } - private set { this.SetValue(SelectedFilterGroupsPropertyKey, value); } - } - - private static readonly DependencyPropertyKey SelectedFilterGroupsPropertyKey = - DependencyProperty.RegisterReadOnly("SelectedFilterGroups", typeof(string), - typeof(Gallery), new UIPropertyMetadata(null)); - - /// - /// Using a DependencyProperty as the backing store for SelectedFilterGroups. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty SelectedFilterGroupsProperty = SelectedFilterGroupsPropertyKey.DependencyProperty; - - /// - /// Gets whether gallery has selected filter - /// - public bool HasFilter - { - get { return (bool)this.GetValue(HasFilterProperty); } - private set { this.SetValue(HasFilterPropertyKey, value); } - } - - private static readonly DependencyPropertyKey HasFilterPropertyKey = DependencyProperty.RegisterReadOnly("HasFilter", typeof(bool), typeof(Gallery), new UIPropertyMetadata(false)); - - /// - /// Using a DependencyProperty as the backing store for HasFilter. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty HasFilterProperty = HasFilterPropertyKey.DependencyProperty; - - void OnFilterMenuItemClick(object sender, RoutedEventArgs e) - { - MenuItem senderItem = (MenuItem)sender; - MenuItem item = this.GetFilterMenuItem(this.SelectedFilter); - item.IsChecked = false; - senderItem.IsChecked = true; - this.SelectedFilter = senderItem.Tag as GalleryGroupFilter; - this.groupsMenuButton.IsDropDownOpen = false; - e.Handled = true; - } - - MenuItem GetFilterMenuItem(GalleryGroupFilter filter) - { - if (filter == null) return null; - return this.groupsMenuButton.Items.Cast().FirstOrDefault(item => (item != null) && (item.Header.ToString() == filter.Title)); - /*foreach (MenuItem item in groupsMenuButton.Items) - { - if ((item!=null)&&(item.Header == filter.Title)) return item; - } - return null;*/ - } - - #endregion - - #region Selectable - - /// - /// Gets or sets whether gallery items can be selected - /// - public bool Selectable - { - get { return (bool)this.GetValue(SelectableProperty); } - set { this.SetValue(SelectableProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Selectable. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty SelectableProperty = - DependencyProperty.Register("Selectable", typeof(bool), - typeof(Gallery), new UIPropertyMetadata(true, OnSelectableChanged)); - - private static void OnSelectableChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - d.CoerceValue(SelectedItemProperty); - } - - #endregion - - #region IsLastItem - - /// - /// Gets whether gallery is last item in ItemsControl - /// - public bool IsLastItem - { - get { return (bool)this.GetValue(IsLastItemProperty); } - private set { this.SetValue(IsLastItemPropertyKey, value); } - } - - /// - /// Using a DependencyProperty as the backing store for IsLastItem. This enables animation, styling, binding, etc... - /// - public static readonly DependencyPropertyKey IsLastItemPropertyKey = DependencyProperty.RegisterReadOnly("IsLastItem", typeof(bool), typeof(Gallery), new UIPropertyMetadata(false)); - /// - /// Using a DependencyProperty as the backing store for IsLastItem. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IsLastItemProperty = IsLastItemPropertyKey.DependencyProperty; - - #endregion - - #endregion - - #region Constructors - - /// - /// Static constructor - /// - [SuppressMessage("Microsoft.Performance", "CA1810")] - static Gallery() - { - Type type = typeof(Gallery); - DefaultStyleKeyProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(typeof(Gallery))); - SelectedItemProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(null, CoerceSelectedItem)); - ContextMenuService.Attach(type); - StyleProperty.OverrideMetadata(typeof(Gallery), new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceStyle))); - } - - // Coerce object style - static object OnCoerceStyle(DependencyObject d, object basevalue) - { - if (basevalue == null) - { - basevalue = (d as FrameworkElement).TryFindResource(typeof(Gallery)); - } - - return basevalue; - } - - // Coerce selected item - private static object CoerceSelectedItem(DependencyObject d, object basevalue) - { - Gallery gallery = (Gallery)d; - - if (!gallery.Selectable) - { - GalleryItem galleryItem = (GalleryItem)gallery.ItemContainerGenerator.ContainerFromItem(basevalue); - - if (basevalue != null && galleryItem != null) - { - galleryItem.IsSelected = false; - } - - return null; - } - - return basevalue; - } - - /// - /// Default constructor - /// - public Gallery() - { - ContextMenuService.Coerce(this); - this.Loaded += this.OnLoaded; - this.Focusable = false; - KeyboardNavigation.SetDirectionalNavigation(this, KeyboardNavigationMode.Continue); - } - - private void OnLoaded(object sender, RoutedEventArgs e) - { - ItemsControl parent = this.Parent as ItemsControl; - if (parent != null) - { - if (parent.Items.IndexOf(this) == parent.Items.Count - 1) - { - this.IsLastItem = true; - } - else - { - this.IsLastItem = false; - } - } - } - - #endregion - - #region Overrides - - /// - /// Creates or identifies the element that is used to display the given item. - /// - /// The element that is used to display the given item. - protected override DependencyObject GetContainerForItemOverride() - { - return new GalleryItem(); - } - - /// - /// Determines if the specified item is (or is eligible to be) its own container. - /// - /// The item to check. - /// - protected override bool IsItemItsOwnContainerOverride(object item) - { - return item is GalleryItem; - } - - /// - /// When overridden in a derived class, is invoked whenever application code or internal processes call . - /// - public override void OnApplyTemplate() - { - if (this.groupsMenuButton != null) - { - this.groupsMenuButton.Items.Clear(); - } - - this.groupsMenuButton = this.GetTemplateChild("PART_DropDownButton") as DropDownButton; - - if (this.groupsMenuButton != null) - { - for (int i = 0; i < this.Filters.Count; i++) - { - var item = new MenuItem - { - Header = this.Filters[i].Title, - Tag = this.Filters[i], - IsDefinitive = false - }; - - if (this.Filters[i] == this.SelectedFilter) - { - item.IsChecked = true; - } - - item.Click += this.OnFilterMenuItemClick; - this.groupsMenuButton.Items.Add(item); - } - } - - base.OnApplyTemplate(); - } - - #endregion - } +using System; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Markup; + +namespace Fluent +{ + // TODO: add TemplatePart's in Gallery (!) + + /// + /// Represents gallery control. + /// Usually a gallery is hosted in context menu + /// + [ContentProperty("Items")] + public class Gallery : ListBox + { + #region Fields + + private ObservableCollection filters; + + private DropDownButton groupsMenuButton; + + #endregion + + #region Properties + + #region MinItemsInRow + + /// + /// Min width of the Gallery + /// + public int MinItemsInRow + { + get { return (int)this.GetValue(MinItemsInRowProperty); } + set { this.SetValue(MinItemsInRowProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for MinItemsInRow. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty MinItemsInRowProperty = + DependencyProperty.Register("MinItemsInRow", typeof(int), + typeof(Gallery), new UIPropertyMetadata(1)); + + #endregion + + #region MaxItemsInRow + + /// + /// Max width of the Gallery + /// + public int MaxItemsInRow + { + get { return (int)this.GetValue(MaxItemsInRowProperty); } + set { this.SetValue(MaxItemsInRowProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for MaxItemsInRow. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty MaxItemsInRowProperty = + DependencyProperty.Register("MaxItemsInRow", typeof(int), + typeof(Gallery), new UIPropertyMetadata(int.MaxValue)); + + #endregion + + #region GroupBy + + /// + /// Gets or sets name of property which + /// will use to group items in the Gallery. + /// + public string GroupBy + { + get { return (string)this.GetValue(GroupByProperty); } + set { this.SetValue(GroupByProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for GroupBy. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty GroupByProperty = + DependencyProperty.Register("GroupBy", typeof(string), typeof(Gallery), + new UIPropertyMetadata(null)); + + #endregion + + #region Orientation + + /// + /// Gets or sets orientation of gallery + /// + public Orientation Orientation + { + get { return (Orientation)this.GetValue(OrientationProperty); } + set { this.SetValue(OrientationProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Orientation. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty OrientationProperty = + DependencyProperty.Register("Orientation", typeof(Orientation), + typeof(Gallery), new UIPropertyMetadata(Orientation.Horizontal)); + + #endregion + + #region ItemWidth + + /// + /// Gets or sets item width + /// + public double ItemWidth + { + get { return (double)this.GetValue(ItemWidthProperty); } + set { this.SetValue(ItemWidthProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for ItemWidth. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty ItemWidthProperty = + DependencyProperty.Register("ItemWidth", typeof(double), typeof(Gallery), new UIPropertyMetadata(double.NaN)); + + /// + /// Gets or sets item height + /// + public double ItemHeight + { + get { return (double)this.GetValue(ItemHeightProperty); } + set { this.SetValue(ItemHeightProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for ItemHeight. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty ItemHeightProperty = + DependencyProperty.Register("ItemHeight", typeof(double), typeof(Gallery), new UIPropertyMetadata(double.NaN)); + + #endregion + + #region Filters + + /// + /// Gets collection of filters + /// + public ObservableCollection Filters + { + get + { + if (this.filters == null) + { + this.filters = new ObservableCollection(); + this.filters.CollectionChanged += this.OnFilterCollectionChanged; + } + return this.filters; + } + } + + // Handle toolbar items changes + void OnFilterCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + this.HasFilter = this.Filters.Count > 0; + this.InvalidateProperty(SelectedFilterProperty); + + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + for (int i = 0; i < e.NewItems.Count; i++) + { + if (this.groupsMenuButton != null) + { + GalleryGroupFilter filter = (GalleryGroupFilter)e.NewItems[i]; + MenuItem menuItem = new MenuItem(); + menuItem.Header = filter.Title; + menuItem.Tag = filter; + if (filter == this.SelectedFilter) menuItem.IsChecked = true; + menuItem.Click += this.OnFilterMenuItemClick; + this.groupsMenuButton.Items.Insert(e.NewStartingIndex + i, menuItem); + } + } + break; + case NotifyCollectionChangedAction.Remove: + foreach (object item in e.OldItems) + { + if (this.groupsMenuButton != null) + { + this.groupsMenuButton.Items.Remove(this.GetFilterMenuItem(item as GalleryGroupFilter)); + } + } + break; + + case NotifyCollectionChangedAction.Replace: + foreach (object item in e.OldItems) + { + if (this.groupsMenuButton != null) + { + this.groupsMenuButton.Items.Remove(this.GetFilterMenuItem(item as GalleryGroupFilter)); + } + } + foreach (var item in e.NewItems.OfType()) + { + if (this.groupsMenuButton != null) + { + GalleryGroupFilter filter = item; + MenuItem menuItem = new MenuItem(); + menuItem.Header = filter.Title; + menuItem.Tag = filter; + if (filter == this.SelectedFilter) menuItem.IsChecked = true; + menuItem.Click += this.OnFilterMenuItemClick; + this.groupsMenuButton.Items.Add(menuItem); + } + } + break; + } + } + + /// + /// Gets or sets selected filter + /// + public GalleryGroupFilter SelectedFilter + { + get { return (GalleryGroupFilter)this.GetValue(SelectedFilterProperty); } + set { this.SetValue(SelectedFilterProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for SelectedFilter. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty SelectedFilterProperty = + DependencyProperty.Register("SelectedFilter", typeof(GalleryGroupFilter), + typeof(Gallery), new UIPropertyMetadata(null, OnFilterChanged, CoerceSelectedFilter)); + + // Coerce selected filter + static object CoerceSelectedFilter(DependencyObject d, object basevalue) + { + Gallery gallery = (Gallery)d; + if ((basevalue == null) && (gallery.Filters.Count > 0)) return gallery.Filters[0]; + return basevalue; + } + + // Handles filter property changed + static void OnFilterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + Gallery gallery = (Gallery)d; + GalleryGroupFilter filter = e.NewValue as GalleryGroupFilter; + if (filter != null) + { + gallery.SelectedFilterTitle = filter.Title; + gallery.SelectedFilterGroups = filter.Groups; + } + else + { + gallery.SelectedFilterTitle = ""; + gallery.SelectedFilterGroups = null; + } + gallery.UpdateLayout(); + } + + /// + /// Gets selected filter title + /// + public string SelectedFilterTitle + { + get { return (string)this.GetValue(SelectedFilterTitleProperty); } + private set { this.SetValue(SelectedFilterTitlePropertyKey, value); } + } + + private static readonly DependencyPropertyKey SelectedFilterTitlePropertyKey = + DependencyProperty.RegisterReadOnly("SelectedFilterTitle", typeof(string), + typeof(Gallery), new UIPropertyMetadata(null)); + + /// + /// Using a DependencyProperty as the backing store for SelectedFilterTitle. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty SelectedFilterTitleProperty = SelectedFilterTitlePropertyKey.DependencyProperty; + + /// + /// Gets selected filter groups + /// + public string SelectedFilterGroups + { + get { return (string)this.GetValue(SelectedFilterGroupsProperty); } + private set { this.SetValue(SelectedFilterGroupsPropertyKey, value); } + } + + private static readonly DependencyPropertyKey SelectedFilterGroupsPropertyKey = + DependencyProperty.RegisterReadOnly("SelectedFilterGroups", typeof(string), + typeof(Gallery), new UIPropertyMetadata(null)); + + /// + /// Using a DependencyProperty as the backing store for SelectedFilterGroups. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty SelectedFilterGroupsProperty = SelectedFilterGroupsPropertyKey.DependencyProperty; + + /// + /// Gets whether gallery has selected filter + /// + public bool HasFilter + { + get { return (bool)this.GetValue(HasFilterProperty); } + private set { this.SetValue(HasFilterPropertyKey, value); } + } + + private static readonly DependencyPropertyKey HasFilterPropertyKey = DependencyProperty.RegisterReadOnly("HasFilter", typeof(bool), typeof(Gallery), new UIPropertyMetadata(false)); + + /// + /// Using a DependencyProperty as the backing store for HasFilter. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty HasFilterProperty = HasFilterPropertyKey.DependencyProperty; + + void OnFilterMenuItemClick(object sender, RoutedEventArgs e) + { + MenuItem senderItem = (MenuItem)sender; + MenuItem item = this.GetFilterMenuItem(this.SelectedFilter); + item.IsChecked = false; + senderItem.IsChecked = true; + this.SelectedFilter = senderItem.Tag as GalleryGroupFilter; + this.groupsMenuButton.IsDropDownOpen = false; + e.Handled = true; + } + + MenuItem GetFilterMenuItem(GalleryGroupFilter filter) + { + if (filter == null) return null; + return this.groupsMenuButton.Items.Cast().FirstOrDefault(item => (item != null) && (item.Header.ToString() == filter.Title)); + /*foreach (MenuItem item in groupsMenuButton.Items) + { + if ((item!=null)&&(item.Header == filter.Title)) return item; + } + return null;*/ + } + + #endregion + + #region Selectable + + /// + /// Gets or sets whether gallery items can be selected + /// + public bool Selectable + { + get { return (bool)this.GetValue(SelectableProperty); } + set { this.SetValue(SelectableProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Selectable. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty SelectableProperty = + DependencyProperty.Register("Selectable", typeof(bool), + typeof(Gallery), new UIPropertyMetadata(true, OnSelectableChanged)); + + private static void OnSelectableChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + d.CoerceValue(SelectedItemProperty); + } + + #endregion + + #region IsLastItem + + /// + /// Gets whether gallery is last item in ItemsControl + /// + public bool IsLastItem + { + get { return (bool)this.GetValue(IsLastItemProperty); } + private set { this.SetValue(IsLastItemPropertyKey, value); } + } + + /// + /// Using a DependencyProperty as the backing store for IsLastItem. This enables animation, styling, binding, etc... + /// + public static readonly DependencyPropertyKey IsLastItemPropertyKey = DependencyProperty.RegisterReadOnly("IsLastItem", typeof(bool), typeof(Gallery), new UIPropertyMetadata(false)); + /// + /// Using a DependencyProperty as the backing store for IsLastItem. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IsLastItemProperty = IsLastItemPropertyKey.DependencyProperty; + + #endregion + + #endregion + + #region Constructors + + /// + /// Static constructor + /// + [SuppressMessage("Microsoft.Performance", "CA1810")] + static Gallery() + { + Type type = typeof(Gallery); + DefaultStyleKeyProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(typeof(Gallery))); + SelectedItemProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(null, CoerceSelectedItem)); + ContextMenuService.Attach(type); + StyleProperty.OverrideMetadata(typeof(Gallery), new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceStyle))); + } + + // Coerce object style + static object OnCoerceStyle(DependencyObject d, object basevalue) + { + if (basevalue == null) + { + basevalue = (d as FrameworkElement).TryFindResource(typeof(Gallery)); + } + + return basevalue; + } + + // Coerce selected item + private static object CoerceSelectedItem(DependencyObject d, object basevalue) + { + Gallery gallery = (Gallery)d; + + if (!gallery.Selectable) + { + GalleryItem galleryItem = (GalleryItem)gallery.ItemContainerGenerator.ContainerFromItem(basevalue); + + if (basevalue != null && galleryItem != null) + { + galleryItem.IsSelected = false; + } + + return null; + } + + return basevalue; + } + + /// + /// Default constructor + /// + public Gallery() + { + ContextMenuService.Coerce(this); + this.Loaded += this.OnLoaded; + this.Focusable = false; + KeyboardNavigation.SetDirectionalNavigation(this, KeyboardNavigationMode.Continue); + } + + private void OnLoaded(object sender, RoutedEventArgs e) + { + ItemsControl parent = this.Parent as ItemsControl; + if (parent != null) + { + if (parent.Items.IndexOf(this) == parent.Items.Count - 1) + { + this.IsLastItem = true; + } + else + { + this.IsLastItem = false; + } + } + } + + #endregion + + #region Overrides + + /// + /// Creates or identifies the element that is used to display the given item. + /// + /// The element that is used to display the given item. + protected override DependencyObject GetContainerForItemOverride() + { + return new GalleryItem(); + } + + /// + /// Determines if the specified item is (or is eligible to be) its own container. + /// + /// The item to check. + /// + protected override bool IsItemItsOwnContainerOverride(object item) + { + return item is GalleryItem; + } + + /// + /// When overridden in a derived class, is invoked whenever application code or internal processes call . + /// + public override void OnApplyTemplate() + { + if (this.groupsMenuButton != null) + { + this.groupsMenuButton.Items.Clear(); + } + + this.groupsMenuButton = this.GetTemplateChild("PART_DropDownButton") as DropDownButton; + + if (this.groupsMenuButton != null) + { + for (int i = 0; i < this.Filters.Count; i++) + { + var item = new MenuItem + { + Header = this.Filters[i].Title, + Tag = this.Filters[i], + IsDefinitive = false + }; + + if (this.Filters[i] == this.SelectedFilter) + { + item.IsChecked = true; + } + + item.Click += this.OnFilterMenuItemClick; + this.groupsMenuButton.Items.Add(item); + } + } + + base.OnApplyTemplate(); + } + + #endregion + } } \ No newline at end of file diff --git a/Fluent/Controls/GalleryGroupContainer.cs b/Fluent.Ribbon/Controls/GalleryGroupContainer.cs similarity index 97% rename from Fluent/Controls/GalleryGroupContainer.cs rename to Fluent.Ribbon/Controls/GalleryGroupContainer.cs index 67a5ed4cc..9785aae58 100644 --- a/Fluent/Controls/GalleryGroupContainer.cs +++ b/Fluent.Ribbon/Controls/GalleryGroupContainer.cs @@ -1,306 +1,306 @@ -using System; -using System.Diagnostics; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Media; -using System.Windows.Threading; - -namespace Fluent -{ - /// - /// Represents container of grouped gallery items in GalleryPanel or Gallery - /// - public class GalleryGroupContainer : HeaderedItemsControl - { - #region Fields - - private Panel previousItemsPanel; - private int previousItemsCount; - - // Whether MaxWidth of the ItemsPanel needs to be updated - private bool maxMinWidthNeedsToBeUpdated; - - #endregion - - #region Properites - - #region IsHeadered - - /// - /// Gets or sets whether the header must be shown. - /// When the property is false this control uses to show all items without grouping - /// - public bool IsHeadered - { - get { return (bool)this.GetValue(IsHeaderedProperty); } - set { this.SetValue(IsHeaderedProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for IsHeadered. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IsHeaderedProperty = - DependencyProperty.Register("IsHeadered", typeof(bool), - typeof(GalleryGroupContainer), new UIPropertyMetadata(true)); - - #endregion - - #region Orientation - - /// - /// Gets or sets panel orientation - /// - public Orientation Orientation - { - get { return (Orientation)this.GetValue(OrientationProperty); } - set { this.SetValue(OrientationProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Orientation. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty OrientationProperty = - DependencyProperty.Register("Orientation", typeof(Orientation), - typeof(GalleryGroupContainer), new UIPropertyMetadata(Orientation.Horizontal)); - - #endregion - - #region ItemWidth - - /// - /// Gets or sets a value that specifies the width of - /// all items that are contained within - /// - public double ItemWidth - { - get { return (double)this.GetValue(ItemWidthProperty); } - set { this.SetValue(ItemWidthProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for ItemWidth. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty ItemWidthProperty = - DependencyProperty.Register("ItemWidth", typeof(double), - typeof(GalleryGroupContainer), new UIPropertyMetadata(double.NaN)); - - #endregion - - #region ItemHeight - - /// - /// Gets or sets a value that specifies the height of - /// all items that are contained within - /// - public double ItemHeight - { - get { return (double)this.GetValue(ItemHeightProperty); } - set { this.SetValue(ItemHeightProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for ItemHeight. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty ItemHeightProperty = - DependencyProperty.Register("ItemHeight", typeof(double), - typeof(GalleryGroupContainer), new UIPropertyMetadata(double.NaN)); - - #endregion - - #region MinItemsInRow - - /// - /// Gets or sets minimum items quantity in row - /// - public int MinItemsInRow - { - get { return (int)this.GetValue(MinItemsInRowProperty); } - set { this.SetValue(MinItemsInRowProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for ItemsInRow. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty MinItemsInRowProperty = - DependencyProperty.Register("MinItemsInRow", typeof(int), - typeof(GalleryGroupContainer), new UIPropertyMetadata(0, OnMaxMinItemsInRowChanged)); - - static void OnMaxMinItemsInRowChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - GalleryGroupContainer galleryGroupContainer = (GalleryGroupContainer) d; - galleryGroupContainer.maxMinWidthNeedsToBeUpdated = true; - } - - #endregion - - #region MaxItemsInRow - - /// - /// Gets or sets maximum items quantity in row - /// - public int MaxItemsInRow - { - get { return (int)this.GetValue(MaxItemsInRowProperty); } - set { this.SetValue(MaxItemsInRowProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for ItemsInRow. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty MaxItemsInRowProperty = - DependencyProperty.Register("MaxItemsInRow", typeof(int), - typeof(GalleryGroupContainer), new UIPropertyMetadata(int.MaxValue, OnMaxMinItemsInRowChanged)); - - #endregion - - #endregion - - #region Initialization - - /// - /// Static constructor - /// - static GalleryGroupContainer() - { - DefaultStyleKeyProperty.OverrideMetadata(typeof(GalleryGroupContainer), new FrameworkPropertyMetadata(typeof(GalleryGroupContainer))); - StyleProperty.OverrideMetadata(typeof(GalleryGroupContainer), new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceStyle))); - } - - // Coerce object style - static object OnCoerceStyle(DependencyObject d, object basevalue) - { - if (basevalue == null) - { - basevalue = ((FrameworkElement)d).TryFindResource(typeof(GalleryGroupContainer)); - } - - return basevalue; - } - - #endregion - - #region MaxWidth Updating - - // Sets MaxWidth of the items panel based of ItemsInRow property - private void UpdateMinAndMaxWidth() - { - this.maxMinWidthNeedsToBeUpdated = false; - - var itemsPanel = FindItemsPanel(this); - if (itemsPanel == null) - { - // Item's panel is not ready now - if (this.IsLoaded) - { - Debug.WriteLine("Panel with IsItemsHost = true is not found in GalleryGroupContainer (probably the style is not correct or haven't attached yet)"); - } - - this.Dispatcher.BeginInvoke((Action)this.InvalidateMeasure, DispatcherPriority.ContextIdle); - return; - } - - if (this.Orientation == Orientation.Vertical) - { - // Min/Max is used for Horizontal layout only - itemsPanel.MinWidth = 0; - itemsPanel.MaxWidth = double.PositiveInfinity; - return; - } - - var itemWidth = this.GetItemWidth(); - if (double.IsNaN(itemWidth)) - { - // We can't calc item's width now - return; - } - - itemsPanel.MinWidth = Math.Min(this.Items.Count, this.MinItemsInRow) * itemWidth + 0.1; - itemsPanel.MaxWidth = Math.Min(this.Items.Count, this.MaxItemsInRow) * itemWidth + 0.1; - } - - /// - /// Determinates item's size (return Size.Empty in case of it is not possible) - /// - /// - public Size GetItemSize() - { - if (!double.IsNaN(this.ItemWidth) - && !double.IsNaN(this.ItemHeight)) - { - return new Size(this.ItemWidth, this.ItemHeight); - } - - if (this.Items.Count == 0) - { - return Size.Empty; - } - - var anItem = this.ItemContainerGenerator.ContainerFromItem(this.Items[0]) as UIElement; - if (anItem == null) - { - return Size.Empty; - } - - anItem.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); - var result = anItem.DesiredSize; - anItem.InvalidateMeasure(); - return result; - } - - // Determinates item's width (return Double.NaN in case of it is not possible) - private double GetItemWidth() - { - return this.GetItemSize().Width; - } - - // Finds panel with IsItemsHost, or null if such panel is not found - private static Panel FindItemsPanel(DependencyObject obj) - { - for (var i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++) - { - var panel = obj as Panel; - if (panel != null && - panel.IsItemsHost) - { - return panel; - } - - panel = FindItemsPanel(VisualTreeHelper.GetChild(obj, i)); - if (panel != null) - { - return panel; - } - } - - return null; - } - - #endregion - - /// - /// Called to remeasure a control. - /// - /// The size of the control, up to the maximum specified by constraint. - /// The maximum size that the method can return. - protected override Size MeasureOverride(Size constraint) - { - var panel = FindItemsPanel(this); - if (panel != this.previousItemsPanel - || this.previousItemsCount != this.Items.Count - || this.maxMinWidthNeedsToBeUpdated) - { - // Track ItemsPanel changing - this.previousItemsPanel = panel; - this.previousItemsCount = this.Items.Count; - this.UpdateMinAndMaxWidth(); - } - return base.MeasureOverride(constraint); - } - } +using System; +using System.Diagnostics; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; +using System.Windows.Threading; + +namespace Fluent +{ + /// + /// Represents container of grouped gallery items in GalleryPanel or Gallery + /// + public class GalleryGroupContainer : HeaderedItemsControl + { + #region Fields + + private Panel previousItemsPanel; + private int previousItemsCount; + + // Whether MaxWidth of the ItemsPanel needs to be updated + private bool maxMinWidthNeedsToBeUpdated; + + #endregion + + #region Properites + + #region IsHeadered + + /// + /// Gets or sets whether the header must be shown. + /// When the property is false this control uses to show all items without grouping + /// + public bool IsHeadered + { + get { return (bool)this.GetValue(IsHeaderedProperty); } + set { this.SetValue(IsHeaderedProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for IsHeadered. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IsHeaderedProperty = + DependencyProperty.Register("IsHeadered", typeof(bool), + typeof(GalleryGroupContainer), new UIPropertyMetadata(true)); + + #endregion + + #region Orientation + + /// + /// Gets or sets panel orientation + /// + public Orientation Orientation + { + get { return (Orientation)this.GetValue(OrientationProperty); } + set { this.SetValue(OrientationProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Orientation. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty OrientationProperty = + DependencyProperty.Register("Orientation", typeof(Orientation), + typeof(GalleryGroupContainer), new UIPropertyMetadata(Orientation.Horizontal)); + + #endregion + + #region ItemWidth + + /// + /// Gets or sets a value that specifies the width of + /// all items that are contained within + /// + public double ItemWidth + { + get { return (double)this.GetValue(ItemWidthProperty); } + set { this.SetValue(ItemWidthProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for ItemWidth. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty ItemWidthProperty = + DependencyProperty.Register("ItemWidth", typeof(double), + typeof(GalleryGroupContainer), new UIPropertyMetadata(double.NaN)); + + #endregion + + #region ItemHeight + + /// + /// Gets or sets a value that specifies the height of + /// all items that are contained within + /// + public double ItemHeight + { + get { return (double)this.GetValue(ItemHeightProperty); } + set { this.SetValue(ItemHeightProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for ItemHeight. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty ItemHeightProperty = + DependencyProperty.Register("ItemHeight", typeof(double), + typeof(GalleryGroupContainer), new UIPropertyMetadata(double.NaN)); + + #endregion + + #region MinItemsInRow + + /// + /// Gets or sets minimum items quantity in row + /// + public int MinItemsInRow + { + get { return (int)this.GetValue(MinItemsInRowProperty); } + set { this.SetValue(MinItemsInRowProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for ItemsInRow. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty MinItemsInRowProperty = + DependencyProperty.Register("MinItemsInRow", typeof(int), + typeof(GalleryGroupContainer), new UIPropertyMetadata(0, OnMaxMinItemsInRowChanged)); + + static void OnMaxMinItemsInRowChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + GalleryGroupContainer galleryGroupContainer = (GalleryGroupContainer) d; + galleryGroupContainer.maxMinWidthNeedsToBeUpdated = true; + } + + #endregion + + #region MaxItemsInRow + + /// + /// Gets or sets maximum items quantity in row + /// + public int MaxItemsInRow + { + get { return (int)this.GetValue(MaxItemsInRowProperty); } + set { this.SetValue(MaxItemsInRowProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for ItemsInRow. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty MaxItemsInRowProperty = + DependencyProperty.Register("MaxItemsInRow", typeof(int), + typeof(GalleryGroupContainer), new UIPropertyMetadata(int.MaxValue, OnMaxMinItemsInRowChanged)); + + #endregion + + #endregion + + #region Initialization + + /// + /// Static constructor + /// + static GalleryGroupContainer() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(GalleryGroupContainer), new FrameworkPropertyMetadata(typeof(GalleryGroupContainer))); + StyleProperty.OverrideMetadata(typeof(GalleryGroupContainer), new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceStyle))); + } + + // Coerce object style + static object OnCoerceStyle(DependencyObject d, object basevalue) + { + if (basevalue == null) + { + basevalue = ((FrameworkElement)d).TryFindResource(typeof(GalleryGroupContainer)); + } + + return basevalue; + } + + #endregion + + #region MaxWidth Updating + + // Sets MaxWidth of the items panel based of ItemsInRow property + private void UpdateMinAndMaxWidth() + { + this.maxMinWidthNeedsToBeUpdated = false; + + var itemsPanel = FindItemsPanel(this); + if (itemsPanel == null) + { + // Item's panel is not ready now + if (this.IsLoaded) + { + Debug.WriteLine("Panel with IsItemsHost = true is not found in GalleryGroupContainer (probably the style is not correct or haven't attached yet)"); + } + + this.Dispatcher.BeginInvoke((Action)this.InvalidateMeasure, DispatcherPriority.ContextIdle); + return; + } + + if (this.Orientation == Orientation.Vertical) + { + // Min/Max is used for Horizontal layout only + itemsPanel.MinWidth = 0; + itemsPanel.MaxWidth = double.PositiveInfinity; + return; + } + + var itemWidth = this.GetItemWidth(); + if (double.IsNaN(itemWidth)) + { + // We can't calc item's width now + return; + } + + itemsPanel.MinWidth = Math.Min(this.Items.Count, this.MinItemsInRow) * itemWidth + 0.1; + itemsPanel.MaxWidth = Math.Min(this.Items.Count, this.MaxItemsInRow) * itemWidth + 0.1; + } + + /// + /// Determinates item's size (return Size.Empty in case of it is not possible) + /// + /// + public Size GetItemSize() + { + if (!double.IsNaN(this.ItemWidth) + && !double.IsNaN(this.ItemHeight)) + { + return new Size(this.ItemWidth, this.ItemHeight); + } + + if (this.Items.Count == 0) + { + return Size.Empty; + } + + var anItem = this.ItemContainerGenerator.ContainerFromItem(this.Items[0]) as UIElement; + if (anItem == null) + { + return Size.Empty; + } + + anItem.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); + var result = anItem.DesiredSize; + anItem.InvalidateMeasure(); + return result; + } + + // Determinates item's width (return Double.NaN in case of it is not possible) + private double GetItemWidth() + { + return this.GetItemSize().Width; + } + + // Finds panel with IsItemsHost, or null if such panel is not found + private static Panel FindItemsPanel(DependencyObject obj) + { + for (var i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++) + { + var panel = obj as Panel; + if (panel != null && + panel.IsItemsHost) + { + return panel; + } + + panel = FindItemsPanel(VisualTreeHelper.GetChild(obj, i)); + if (panel != null) + { + return panel; + } + } + + return null; + } + + #endregion + + /// + /// Called to remeasure a control. + /// + /// The size of the control, up to the maximum specified by constraint. + /// The maximum size that the method can return. + protected override Size MeasureOverride(Size constraint) + { + var panel = FindItemsPanel(this); + if (panel != this.previousItemsPanel + || this.previousItemsCount != this.Items.Count + || this.maxMinWidthNeedsToBeUpdated) + { + // Track ItemsPanel changing + this.previousItemsPanel = panel; + this.previousItemsCount = this.Items.Count; + this.UpdateMinAndMaxWidth(); + } + return base.MeasureOverride(constraint); + } + } } \ No newline at end of file diff --git a/Fluent/Controls/GalleryGroupFilter.cs b/Fluent.Ribbon/Controls/GalleryGroupFilter.cs similarity index 97% rename from Fluent/Controls/GalleryGroupFilter.cs rename to Fluent.Ribbon/Controls/GalleryGroupFilter.cs index edbbf9b45..b0e27fe4b 100644 --- a/Fluent/Controls/GalleryGroupFilter.cs +++ b/Fluent.Ribbon/Controls/GalleryGroupFilter.cs @@ -1,44 +1,44 @@ -using System.Windows; - -namespace Fluent -{ - /// - /// Represents gallery group filter definition - /// - public class GalleryGroupFilter : DependencyObject - { - /// - /// Gets or sets title of filter - /// - public string Title - { - get { return (string)this.GetValue(TitleProperty); } - set { this.SetValue(TitleProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Title. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty TitleProperty = - DependencyProperty.Register("Title", typeof(string), - typeof(GalleryGroupFilter), new UIPropertyMetadata("GalleryGroupFilter")); - - /// - /// Gets or sets list pf groups splitted by comma - /// - public string Groups - { - get { return (string)this.GetValue(GroupsProperty); } - set { this.SetValue(GroupsProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for ContextualGroups. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty GroupsProperty = - DependencyProperty.Register("ContextualGroups", typeof(string), - typeof(GalleryGroupFilter), new UIPropertyMetadata(string.Empty)); - } +using System.Windows; + +namespace Fluent +{ + /// + /// Represents gallery group filter definition + /// + public class GalleryGroupFilter : DependencyObject + { + /// + /// Gets or sets title of filter + /// + public string Title + { + get { return (string)this.GetValue(TitleProperty); } + set { this.SetValue(TitleProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Title. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty TitleProperty = + DependencyProperty.Register("Title", typeof(string), + typeof(GalleryGroupFilter), new UIPropertyMetadata("GalleryGroupFilter")); + + /// + /// Gets or sets list pf groups splitted by comma + /// + public string Groups + { + get { return (string)this.GetValue(GroupsProperty); } + set { this.SetValue(GroupsProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for ContextualGroups. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty GroupsProperty = + DependencyProperty.Register("ContextualGroups", typeof(string), + typeof(GalleryGroupFilter), new UIPropertyMetadata(string.Empty)); + } } \ No newline at end of file diff --git a/Fluent/Controls/GalleryGroupIcon.cs b/Fluent.Ribbon/Controls/GalleryGroupIcon.cs similarity index 97% rename from Fluent/Controls/GalleryGroupIcon.cs rename to Fluent.Ribbon/Controls/GalleryGroupIcon.cs index 60a3cf091..6b9237b9b 100644 --- a/Fluent/Controls/GalleryGroupIcon.cs +++ b/Fluent.Ribbon/Controls/GalleryGroupIcon.cs @@ -1,46 +1,46 @@ -using System.Windows; -using System.Windows.Media; - -namespace Fluent -{ - /// - /// Represents gallery group icon definition - /// - public class GalleryGroupIcon : DependencyObject - { - /// - /// Gets or sets group name - /// - public string GroupName - { - get { return (string)this.GetValue(GroupNameProperty); } - set { this.SetValue(GroupNameProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for GroupName. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty GroupNameProperty = - DependencyProperty.Register("GroupName", typeof(string), - typeof(GalleryGroupIcon), new UIPropertyMetadata(null)); - - - /// - /// Gets or sets group icon - /// - public ImageSource Icon - { - get { return (ImageSource)this.GetValue(IconProperty); } - set { this.SetValue(IconProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Icon. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IconProperty = - DependencyProperty.Register("Icon", typeof(ImageSource), typeof(GalleryGroupIcon), - new UIPropertyMetadata(null)); - } +using System.Windows; +using System.Windows.Media; + +namespace Fluent +{ + /// + /// Represents gallery group icon definition + /// + public class GalleryGroupIcon : DependencyObject + { + /// + /// Gets or sets group name + /// + public string GroupName + { + get { return (string)this.GetValue(GroupNameProperty); } + set { this.SetValue(GroupNameProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for GroupName. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty GroupNameProperty = + DependencyProperty.Register("GroupName", typeof(string), + typeof(GalleryGroupIcon), new UIPropertyMetadata(null)); + + + /// + /// Gets or sets group icon + /// + public ImageSource Icon + { + get { return (ImageSource)this.GetValue(IconProperty); } + set { this.SetValue(IconProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Icon. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IconProperty = + DependencyProperty.Register("Icon", typeof(ImageSource), typeof(GalleryGroupIcon), + new UIPropertyMetadata(null)); + } } \ No newline at end of file diff --git a/Fluent/Controls/GalleryItem.cs b/Fluent.Ribbon/Controls/GalleryItem.cs similarity index 97% rename from Fluent/Controls/GalleryItem.cs rename to Fluent.Ribbon/Controls/GalleryItem.cs index 205de4ff0..92c45f576 100644 --- a/Fluent/Controls/GalleryItem.cs +++ b/Fluent.Ribbon/Controls/GalleryItem.cs @@ -1,466 +1,466 @@ -using System; -using System.ComponentModel; -using System.Diagnostics.CodeAnalysis; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Controls.Primitives; -using System.Windows.Input; - -namespace Fluent -{ - using Fluent.Internal; - - /// - /// Represents gallery item - /// - public class GalleryItem : ListBoxItem, IKeyTipedControl - { - #region Properties - - #region KeyTip - - /// - /// Gets or sets KeyTip for element. - /// - public string KeyTip - { - get { return (string)this.GetValue(KeyTipProperty); } - set { this.SetValue(KeyTipProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Keys. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty KeyTipProperty = Fluent.KeyTip.KeysProperty.AddOwner(typeof(GalleryItem)); - - #endregion - - /// - /// Gets a value that indicates whether a Button is currently activated. - /// This is a dependency property. - /// - public bool IsPressed - { - get { return (bool)this.GetValue(IsPressedProperty); } - private set { this.SetValue(IsPressedPropertyKey, value); } - } - - private static readonly DependencyPropertyKey IsPressedPropertyKey = - DependencyProperty.RegisterReadOnly("IsPressed", typeof(bool), - typeof(GalleryItem), new UIPropertyMetadata(false)); - - /// - /// Using a DependencyProperty as the backing store for IsPressed. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IsPressedProperty = IsPressedPropertyKey.DependencyProperty; - - /// - /// Gets or sets GalleryItem group - /// - public string Group - { - get { return (string)this.GetValue(GroupProperty); } - set { this.SetValue(GroupProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Group. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty GroupProperty = - DependencyProperty.Register("Group", typeof(string), - typeof(GalleryItem), new UIPropertyMetadata(null)); - - - #region Command - - private bool currentCanExecute = true; - - /// - /// Gets or sets the command to invoke when this button is pressed. This is a dependency property. - /// - [Category("Action"), Localizability(LocalizationCategory.NeverLocalize), Bindable(true)] - public ICommand Command - { - get - { - return (ICommand)this.GetValue(CommandProperty); - } - set - { - this.SetValue(CommandProperty, value); - } - } - - /// - /// Gets or sets the parameter to pass to the System.Windows.Controls.Primitives.ButtonBase.Command property. This is a dependency property. - /// - [Bindable(true), Localizability(LocalizationCategory.NeverLocalize), Category("Action")] - public object CommandParameter - { - get - { - return this.GetValue(CommandParameterProperty); - } - set - { - this.SetValue(CommandParameterProperty, value); - } - } - - /// - /// Gets or sets the element on which to raise the specified command. This is a dependency property. - /// - [Bindable(true), Category("Action")] - public IInputElement CommandTarget - { - get - { - return (IInputElement)this.GetValue(CommandTargetProperty); - } - set - { - this.SetValue(CommandTargetProperty, value); - } - } - - /// - /// Identifies the CommandParameter dependency property. - /// - public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register("CommandParameter", typeof(object), typeof(GalleryItem), new FrameworkPropertyMetadata(null)); - - /// - /// Identifies the routed Command dependency property. - /// - public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(GalleryItem), new FrameworkPropertyMetadata(null, OnCommandChanged)); - - /// - /// Identifies the CommandTarget dependency property. - /// - public static readonly DependencyProperty CommandTargetProperty = DependencyProperty.Register("CommandTarget", typeof(IInputElement), typeof(GalleryItem), new FrameworkPropertyMetadata(null)); - - /// - /// Gets or sets the command to invoke when mouse enters or leaves this button. The commandparameter will be the instance. - /// This is a dependency property. - /// - [Bindable(true), Category("Action")] - public ICommand PreviewCommand - { - get { return (ICommand)this.GetValue(PreviewCommandProperty); } - set { this.SetValue(PreviewCommandProperty, value); } - } - - /// - /// Identifies the PreviewCommand dependency property. - /// - public static readonly DependencyProperty PreviewCommandProperty = - DependencyProperty.Register("PreviewCommand", typeof(ICommand), typeof(GalleryItem), new PropertyMetadata(null)); - - /// - /// Gets or sets the command to invoke when mouse enters or leaves this button. The commandparameter will be the instance. - /// This is a dependency property. - /// - [Bindable(true), Category("Action")] - public ICommand CancelPreviewCommand - { - get { return (ICommand)this.GetValue(CancelPreviewCommandProperty); } - set { this.SetValue(CancelPreviewCommandProperty, value); } - } - - /// - /// Identifies the PreviewCommand dependency property. - /// - public static readonly DependencyProperty CancelPreviewCommandProperty = - DependencyProperty.Register("CancelPreviewCommand", typeof(ICommand), typeof(GalleryItem), new PropertyMetadata(null)); - - /// - /// Handles Command changed - /// - /// - /// - private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var control = d as GalleryItem; - if (control == null) - { - return; - } - - var oldCommand = e.OldValue as ICommand; - if (oldCommand != null) - { - oldCommand.CanExecuteChanged -= control.OnCommandCanExecuteChanged; - } - - var newCommand = e.NewValue as ICommand; - if (newCommand != null) - { - newCommand.CanExecuteChanged += control.OnCommandCanExecuteChanged; - } - - control.UpdateCanExecute(); - } - /// - /// Handles Command CanExecute changed - /// - /// - /// - private void OnCommandCanExecuteChanged(object sender, EventArgs e) - { - this.UpdateCanExecute(); - } - - private void UpdateCanExecute() - { - var canExecute = this.Command != null - && this.CanExecuteCommand(); - if (this.currentCanExecute != canExecute) - { - this.currentCanExecute = canExecute; - this.CoerceValue(IsEnabledProperty); - } - } - - /// - /// Execute command - /// - protected void ExecuteCommand() - { - CommandHelper.Execute(this.Command, this.CommandParameter, this.CommandTarget); - } - - /// - /// Determines whether the Command can be executed - /// - /// Returns Command CanExecute - protected bool CanExecuteCommand() - { - return CommandHelper.CanExecute(this.Command, this.CommandParameter, this.CommandTarget); - } - - #endregion - - #region IsEnabled - - /// - /// Gets a value that becomes the return - /// value of IsEnabled in derived classes. - /// - /// - /// true if the element is enabled; otherwise, false. - /// - protected override bool IsEnabledCore - { - get - { - return (base.IsEnabledCore && (this.currentCanExecute || this.Command == null)); - } - } - - #endregion - - #endregion - - #region Events - - #region Click - - /// - /// Occurs when a RibbonControl is clicked. - /// - [Category("Behavior")] - public event RoutedEventHandler Click - { - add - { - this.AddHandler(ClickEvent, value); - } - remove - { - this.RemoveHandler(ClickEvent, value); - } - } - - /// - /// Identifies the RibbonControl.Click routed event. - /// - public static readonly RoutedEvent ClickEvent = EventManager.RegisterRoutedEvent("Click", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(GalleryItem)); - - /// - /// Raises click event - /// - [SuppressMessage("Microsoft.Design", "CA1030")] - public void RaiseClick() - { - this.RaiseEvent(new RoutedEventArgs(ClickEvent, this)); - } - - #endregion - - #endregion - - #region Constructors - - /// - /// Static constructor - /// - [SuppressMessage("Microsoft.Performance", "CA1810")] - static GalleryItem() - { - DefaultStyleKeyProperty.OverrideMetadata(typeof(GalleryItem), new FrameworkPropertyMetadata(typeof(GalleryItem))); - IsSelectedProperty.AddOwner(typeof(GalleryItem), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.None, OnIsSelectedPropertyChanged)); - StyleProperty.OverrideMetadata(typeof(GalleryItem), new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceStyle))); - } - - // Coerce object style - static object OnCoerceStyle(DependencyObject d, object basevalue) - { - if (basevalue == null) - { - basevalue = (d as FrameworkElement).TryFindResource(typeof(GalleryItem)); - } - - return basevalue; - } - - static void OnIsSelectedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - if ((bool)e.NewValue) - { - ((GalleryItem)d).BringIntoView(); - - var parentSelector = ItemsControl.ItemsControlFromItemContainer(d) as Selector; - - if (parentSelector != null) - { - var item = parentSelector.ItemContainerGenerator.ItemFromContainer(d); - - if (ReferenceEquals(parentSelector.SelectedItem, item) == false) - { - parentSelector.SelectedItem = item; - } - } - } - } - - /// - /// Default constructor - /// - public GalleryItem() - { - this.Click += this.OnClick; - } - - #endregion - - #region Overrides - - /// - /// Provides class handling for the System.Windows.UIElement.MouseLeftButtonDown routed event that occurs - /// when the left mouse button is pressed while the mouse pointer is over this control. - /// - /// The event data. - protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) - { - this.IsPressed = true; - Mouse.Capture(this); - e.Handled = true; - } - - /// - /// Invoked when an unhandled  attached event reaches an element in its route that is derived from this class. Implement this method to add class handling for this event. - /// - /// The that contains event data. - protected override void OnLostMouseCapture(MouseEventArgs e) - { - base.OnLostMouseCapture(e); - - this.IsPressed = false; - } - - /// - /// Provides class handling for the System.Windows.UIElement.MouseLeftButtonUp routed event that occurs - /// when the left mouse button is released while the mouse pointer is over this control. - /// - /// The event data. - protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e) - { - this.IsPressed = false; - if (Mouse.Captured == this) - { - Mouse.Capture(null); - } - - var position = Mouse.PrimaryDevice.GetPosition(this); - - if ((position.X >= 0.0 && position.X <= this.ActualWidth) - && (position.Y >= 0.0 && position.Y <= this.ActualHeight) - && e.ClickCount == 1) - { - this.RaiseClick(); - e.Handled = true; - } - - e.Handled = true; - } - - /// - /// Called when the mouse enters a . - /// - /// The event data. - protected override void OnMouseEnter(MouseEventArgs e) - { - base.OnMouseEnter(e); - - CommandHelper.Execute(this.PreviewCommand, this, null); - } - - /// - /// Called when the mouse leaves a . - /// - /// The event data. - protected override void OnMouseLeave(MouseEventArgs e) - { - base.OnMouseLeave(e); - - CommandHelper.Execute(this.CancelPreviewCommand, this, null); - } - - #endregion - - #region Protected methods - - /// - /// Handles click event - /// - /// Sender - /// The event data - protected virtual void OnClick(object sender, RoutedEventArgs e) - { - PopupService.RaiseDismissPopupEvent(sender, DismissPopupMode.Always); - - this.ExecuteCommand(); - this.IsSelected = true; - e.Handled = true; - } - - #endregion - - /// - /// Handles key tip pressed - /// - public void OnKeyTipPressed() - { - this.RaiseClick(); - } - - /// - /// Handles back navigation with KeyTips - /// - public void OnKeyTipBack() - { - } - } +using System; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Input; + +namespace Fluent +{ + using Fluent.Internal; + + /// + /// Represents gallery item + /// + public class GalleryItem : ListBoxItem, IKeyTipedControl + { + #region Properties + + #region KeyTip + + /// + /// Gets or sets KeyTip for element. + /// + public string KeyTip + { + get { return (string)this.GetValue(KeyTipProperty); } + set { this.SetValue(KeyTipProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Keys. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty KeyTipProperty = Fluent.KeyTip.KeysProperty.AddOwner(typeof(GalleryItem)); + + #endregion + + /// + /// Gets a value that indicates whether a Button is currently activated. + /// This is a dependency property. + /// + public bool IsPressed + { + get { return (bool)this.GetValue(IsPressedProperty); } + private set { this.SetValue(IsPressedPropertyKey, value); } + } + + private static readonly DependencyPropertyKey IsPressedPropertyKey = + DependencyProperty.RegisterReadOnly("IsPressed", typeof(bool), + typeof(GalleryItem), new UIPropertyMetadata(false)); + + /// + /// Using a DependencyProperty as the backing store for IsPressed. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IsPressedProperty = IsPressedPropertyKey.DependencyProperty; + + /// + /// Gets or sets GalleryItem group + /// + public string Group + { + get { return (string)this.GetValue(GroupProperty); } + set { this.SetValue(GroupProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Group. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty GroupProperty = + DependencyProperty.Register("Group", typeof(string), + typeof(GalleryItem), new UIPropertyMetadata(null)); + + + #region Command + + private bool currentCanExecute = true; + + /// + /// Gets or sets the command to invoke when this button is pressed. This is a dependency property. + /// + [Category("Action"), Localizability(LocalizationCategory.NeverLocalize), Bindable(true)] + public ICommand Command + { + get + { + return (ICommand)this.GetValue(CommandProperty); + } + set + { + this.SetValue(CommandProperty, value); + } + } + + /// + /// Gets or sets the parameter to pass to the System.Windows.Controls.Primitives.ButtonBase.Command property. This is a dependency property. + /// + [Bindable(true), Localizability(LocalizationCategory.NeverLocalize), Category("Action")] + public object CommandParameter + { + get + { + return this.GetValue(CommandParameterProperty); + } + set + { + this.SetValue(CommandParameterProperty, value); + } + } + + /// + /// Gets or sets the element on which to raise the specified command. This is a dependency property. + /// + [Bindable(true), Category("Action")] + public IInputElement CommandTarget + { + get + { + return (IInputElement)this.GetValue(CommandTargetProperty); + } + set + { + this.SetValue(CommandTargetProperty, value); + } + } + + /// + /// Identifies the CommandParameter dependency property. + /// + public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register("CommandParameter", typeof(object), typeof(GalleryItem), new FrameworkPropertyMetadata(null)); + + /// + /// Identifies the routed Command dependency property. + /// + public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(GalleryItem), new FrameworkPropertyMetadata(null, OnCommandChanged)); + + /// + /// Identifies the CommandTarget dependency property. + /// + public static readonly DependencyProperty CommandTargetProperty = DependencyProperty.Register("CommandTarget", typeof(IInputElement), typeof(GalleryItem), new FrameworkPropertyMetadata(null)); + + /// + /// Gets or sets the command to invoke when mouse enters or leaves this button. The commandparameter will be the instance. + /// This is a dependency property. + /// + [Bindable(true), Category("Action")] + public ICommand PreviewCommand + { + get { return (ICommand)this.GetValue(PreviewCommandProperty); } + set { this.SetValue(PreviewCommandProperty, value); } + } + + /// + /// Identifies the PreviewCommand dependency property. + /// + public static readonly DependencyProperty PreviewCommandProperty = + DependencyProperty.Register("PreviewCommand", typeof(ICommand), typeof(GalleryItem), new PropertyMetadata(null)); + + /// + /// Gets or sets the command to invoke when mouse enters or leaves this button. The commandparameter will be the instance. + /// This is a dependency property. + /// + [Bindable(true), Category("Action")] + public ICommand CancelPreviewCommand + { + get { return (ICommand)this.GetValue(CancelPreviewCommandProperty); } + set { this.SetValue(CancelPreviewCommandProperty, value); } + } + + /// + /// Identifies the PreviewCommand dependency property. + /// + public static readonly DependencyProperty CancelPreviewCommandProperty = + DependencyProperty.Register("CancelPreviewCommand", typeof(ICommand), typeof(GalleryItem), new PropertyMetadata(null)); + + /// + /// Handles Command changed + /// + /// + /// + private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var control = d as GalleryItem; + if (control == null) + { + return; + } + + var oldCommand = e.OldValue as ICommand; + if (oldCommand != null) + { + oldCommand.CanExecuteChanged -= control.OnCommandCanExecuteChanged; + } + + var newCommand = e.NewValue as ICommand; + if (newCommand != null) + { + newCommand.CanExecuteChanged += control.OnCommandCanExecuteChanged; + } + + control.UpdateCanExecute(); + } + /// + /// Handles Command CanExecute changed + /// + /// + /// + private void OnCommandCanExecuteChanged(object sender, EventArgs e) + { + this.UpdateCanExecute(); + } + + private void UpdateCanExecute() + { + var canExecute = this.Command != null + && this.CanExecuteCommand(); + if (this.currentCanExecute != canExecute) + { + this.currentCanExecute = canExecute; + this.CoerceValue(IsEnabledProperty); + } + } + + /// + /// Execute command + /// + protected void ExecuteCommand() + { + CommandHelper.Execute(this.Command, this.CommandParameter, this.CommandTarget); + } + + /// + /// Determines whether the Command can be executed + /// + /// Returns Command CanExecute + protected bool CanExecuteCommand() + { + return CommandHelper.CanExecute(this.Command, this.CommandParameter, this.CommandTarget); + } + + #endregion + + #region IsEnabled + + /// + /// Gets a value that becomes the return + /// value of IsEnabled in derived classes. + /// + /// + /// true if the element is enabled; otherwise, false. + /// + protected override bool IsEnabledCore + { + get + { + return (base.IsEnabledCore && (this.currentCanExecute || this.Command == null)); + } + } + + #endregion + + #endregion + + #region Events + + #region Click + + /// + /// Occurs when a RibbonControl is clicked. + /// + [Category("Behavior")] + public event RoutedEventHandler Click + { + add + { + this.AddHandler(ClickEvent, value); + } + remove + { + this.RemoveHandler(ClickEvent, value); + } + } + + /// + /// Identifies the RibbonControl.Click routed event. + /// + public static readonly RoutedEvent ClickEvent = EventManager.RegisterRoutedEvent("Click", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(GalleryItem)); + + /// + /// Raises click event + /// + [SuppressMessage("Microsoft.Design", "CA1030")] + public void RaiseClick() + { + this.RaiseEvent(new RoutedEventArgs(ClickEvent, this)); + } + + #endregion + + #endregion + + #region Constructors + + /// + /// Static constructor + /// + [SuppressMessage("Microsoft.Performance", "CA1810")] + static GalleryItem() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(GalleryItem), new FrameworkPropertyMetadata(typeof(GalleryItem))); + IsSelectedProperty.AddOwner(typeof(GalleryItem), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.None, OnIsSelectedPropertyChanged)); + StyleProperty.OverrideMetadata(typeof(GalleryItem), new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceStyle))); + } + + // Coerce object style + static object OnCoerceStyle(DependencyObject d, object basevalue) + { + if (basevalue == null) + { + basevalue = (d as FrameworkElement).TryFindResource(typeof(GalleryItem)); + } + + return basevalue; + } + + static void OnIsSelectedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if ((bool)e.NewValue) + { + ((GalleryItem)d).BringIntoView(); + + var parentSelector = ItemsControl.ItemsControlFromItemContainer(d) as Selector; + + if (parentSelector != null) + { + var item = parentSelector.ItemContainerGenerator.ItemFromContainer(d); + + if (ReferenceEquals(parentSelector.SelectedItem, item) == false) + { + parentSelector.SelectedItem = item; + } + } + } + } + + /// + /// Default constructor + /// + public GalleryItem() + { + this.Click += this.OnClick; + } + + #endregion + + #region Overrides + + /// + /// Provides class handling for the System.Windows.UIElement.MouseLeftButtonDown routed event that occurs + /// when the left mouse button is pressed while the mouse pointer is over this control. + /// + /// The event data. + protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) + { + this.IsPressed = true; + Mouse.Capture(this); + e.Handled = true; + } + + /// + /// Invoked when an unhandled  attached event reaches an element in its route that is derived from this class. Implement this method to add class handling for this event. + /// + /// The that contains event data. + protected override void OnLostMouseCapture(MouseEventArgs e) + { + base.OnLostMouseCapture(e); + + this.IsPressed = false; + } + + /// + /// Provides class handling for the System.Windows.UIElement.MouseLeftButtonUp routed event that occurs + /// when the left mouse button is released while the mouse pointer is over this control. + /// + /// The event data. + protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e) + { + this.IsPressed = false; + if (Mouse.Captured == this) + { + Mouse.Capture(null); + } + + var position = Mouse.PrimaryDevice.GetPosition(this); + + if ((position.X >= 0.0 && position.X <= this.ActualWidth) + && (position.Y >= 0.0 && position.Y <= this.ActualHeight) + && e.ClickCount == 1) + { + this.RaiseClick(); + e.Handled = true; + } + + e.Handled = true; + } + + /// + /// Called when the mouse enters a . + /// + /// The event data. + protected override void OnMouseEnter(MouseEventArgs e) + { + base.OnMouseEnter(e); + + CommandHelper.Execute(this.PreviewCommand, this, null); + } + + /// + /// Called when the mouse leaves a . + /// + /// The event data. + protected override void OnMouseLeave(MouseEventArgs e) + { + base.OnMouseLeave(e); + + CommandHelper.Execute(this.CancelPreviewCommand, this, null); + } + + #endregion + + #region Protected methods + + /// + /// Handles click event + /// + /// Sender + /// The event data + protected virtual void OnClick(object sender, RoutedEventArgs e) + { + PopupService.RaiseDismissPopupEvent(sender, DismissPopupMode.Always); + + this.ExecuteCommand(); + this.IsSelected = true; + e.Handled = true; + } + + #endregion + + /// + /// Handles key tip pressed + /// + public void OnKeyTipPressed() + { + this.RaiseClick(); + } + + /// + /// Handles back navigation with KeyTips + /// + public void OnKeyTipBack() + { + } + } } \ No newline at end of file diff --git a/Fluent/Controls/GalleryItemPlaceholder.cs b/Fluent.Ribbon/Controls/GalleryItemPlaceholder.cs similarity index 96% rename from Fluent/Controls/GalleryItemPlaceholder.cs rename to Fluent.Ribbon/Controls/GalleryItemPlaceholder.cs index 4a5ccf858..ae2a0e74e 100644 --- a/Fluent/Controls/GalleryItemPlaceholder.cs +++ b/Fluent.Ribbon/Controls/GalleryItemPlaceholder.cs @@ -1,96 +1,96 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Text; -using System.Windows; -using System.Windows.Media; - -namespace Fluent -{ - /// - /// Represents internal class to use it in - /// GalleryPanel as placeholder for GalleryItems - /// - class GalleryItemPlaceholder : UIElement - { - #region Fields - - UIElement target; - - #endregion - - #region Properties - - /// - /// Gets the target of the placeholder - /// - public UIElement Target - { - get { return this.target; } - } - - public Size ArrangedSize { get; private set; } - - #endregion - - #region Initialization - - /// - /// Constructor - /// - /// Target - public GalleryItemPlaceholder(UIElement target) - { - this.target = target; - } - - #endregion - - #region Methods - - /// - /// When overridden in a derived class, measures the size in layout - /// required for child elements and determines a size for the derived class. - /// - /// - /// The size that this element determines it needs during layout, - /// based on its calculations of child element sizes. - /// - /// The available size that this element can - /// give to child elements. Infinity can be specified as a value to - /// indicate that the element will size to whatever content is available. - protected override Size MeasureCore(Size availableSize) - { - this.target.Measure(availableSize); - return this.target.DesiredSize; - } - - /// - /// Defines the template for WPF core-level arrange layout definition. - /// - /// - /// The final area within the parent that element should use to - /// arrange itself and its child elements. - protected override void ArrangeCore(Rect finalRect) - { - base.ArrangeCore(finalRect); - - // Remember arranged size to arrange - // targets in GalleryPanel lately - this.ArrangedSize = finalRect.Size; - } - - #endregion - - #region Debug - - /* FOR DEGUG - protected override void OnRender(DrawingContext drawingContext) - { - drawingContext.DrawRectangle(null, new Pen(Brushes.Red, 1), new Rect(RenderSize)); - }*/ - - #endregion - } -} +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Media; + +namespace Fluent +{ + /// + /// Represents internal class to use it in + /// GalleryPanel as placeholder for GalleryItems + /// + class GalleryItemPlaceholder : UIElement + { + #region Fields + + UIElement target; + + #endregion + + #region Properties + + /// + /// Gets the target of the placeholder + /// + public UIElement Target + { + get { return this.target; } + } + + public Size ArrangedSize { get; private set; } + + #endregion + + #region Initialization + + /// + /// Constructor + /// + /// Target + public GalleryItemPlaceholder(UIElement target) + { + this.target = target; + } + + #endregion + + #region Methods + + /// + /// When overridden in a derived class, measures the size in layout + /// required for child elements and determines a size for the derived class. + /// + /// + /// The size that this element determines it needs during layout, + /// based on its calculations of child element sizes. + /// + /// The available size that this element can + /// give to child elements. Infinity can be specified as a value to + /// indicate that the element will size to whatever content is available. + protected override Size MeasureCore(Size availableSize) + { + this.target.Measure(availableSize); + return this.target.DesiredSize; + } + + /// + /// Defines the template for WPF core-level arrange layout definition. + /// + /// + /// The final area within the parent that element should use to + /// arrange itself and its child elements. + protected override void ArrangeCore(Rect finalRect) + { + base.ArrangeCore(finalRect); + + // Remember arranged size to arrange + // targets in GalleryPanel lately + this.ArrangedSize = finalRect.Size; + } + + #endregion + + #region Debug + + /* FOR DEGUG + protected override void OnRender(DrawingContext drawingContext) + { + drawingContext.DrawRectangle(null, new Pen(Brushes.Red, 1), new Rect(RenderSize)); + }*/ + + #endregion + } +} diff --git a/Fluent/Controls/GalleryPanel.cs b/Fluent.Ribbon/Controls/GalleryPanel.cs similarity index 97% rename from Fluent/Controls/GalleryPanel.cs rename to Fluent.Ribbon/Controls/GalleryPanel.cs index e34ca5453..0e47713d9 100644 --- a/Fluent/Controls/GalleryPanel.cs +++ b/Fluent.Ribbon/Controls/GalleryPanel.cs @@ -1,623 +1,623 @@ -namespace Fluent -{ - using System; - using System.Collections; - using System.Collections.Generic; - using System.Linq; - using System.Reflection; - using System.Windows; - using System.Windows.Controls; - using System.Windows.Controls.Primitives; - using System.Windows.Data; - using System.Windows.Media; - using System.Windows.Threading; - - /// - /// Represents panel for Gallery, InRibbonGallery, ComboBox - /// with grouping and filtering capabilities - /// - public class GalleryPanel : VirtualizingStackPanel - { - #region Fields - - // Currently used group containers - private readonly List galleryGroupContainers = new List(); - - // Designate that gallery panel must be refreshed its groups - private bool haveToBeRefreshed; - - // Group name resolver - private Func groupByAdvanced; - - #endregion - - #region Properties - - #region IsGrouped - - /// - /// Gets or sets whether gallery panel shows groups - /// (Filter property still works as usual) - /// - public bool IsGrouped - { - get { return (bool)this.GetValue(IsGroupedProperty); } - set { this.SetValue(IsGroupedProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for IsGrouped. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IsGroupedProperty = - DependencyProperty.Register("IsGrouped", typeof(bool), typeof(GalleryPanel), - new UIPropertyMetadata(true, OnIsGroupedChanged)); - - private static void OnIsGroupedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var galleryPanel = (GalleryPanel)d; - galleryPanel.Invalidate(); - } - - #endregion - - #region GroupBy - - /// - /// Gets or sets property name to group items - /// - public string GroupBy - { - get { return (string)this.GetValue(GroupByProperty); } - set { this.SetValue(GroupByProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for GroupBy. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty GroupByProperty = - DependencyProperty.Register("GroupBy", typeof(string), typeof(GalleryPanel), - new UIPropertyMetadata(null, OnGroupByChanged)); - - private static void OnGroupByChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var galleryPanel = (GalleryPanel)d; - galleryPanel.Invalidate(); - } - - #endregion - - #region GroupByAdvanced - - /// - /// Gets or sets custom user method to group items. - /// If this property is not null, GroupBy property is ignored - /// - public Func GroupByAdvanced - { - get { return this.groupByAdvanced; } - set - { - this.groupByAdvanced = value; - this.Invalidate(); - } - } - - #endregion - - #region GroupStyle - - /// - /// Gets or sets group style - /// - public Style GroupStyle - { - get { return (Style)this.GetValue(GroupStyleProperty); } - set { this.SetValue(GroupStyleProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for GroupHeaderStyle. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty GroupStyleProperty = - DependencyProperty.Register("GroupHeaderStyle", typeof(Style), - typeof(GalleryPanel), new UIPropertyMetadata(null)); - - #endregion - - #region ItemWidth - - /// - /// Gets or sets a value that specifies the width of - /// all items that are contained within - /// - public double ItemWidth - { - get { return (double)this.GetValue(ItemWidthProperty); } - set { this.SetValue(ItemWidthProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for ItemWidth. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty ItemWidthProperty = - DependencyProperty.Register("ItemWidth", typeof(double), - typeof(GalleryPanel), new UIPropertyMetadata(double.NaN)); - - #endregion - - #region ItemHeight - - /// - /// Gets or sets a value that specifies the height of - /// all items that are contained within - /// - public double ItemHeight - { - get { return (double)this.GetValue(ItemHeightProperty); } - set { this.SetValue(ItemHeightProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for ItemHeight. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty ItemHeightProperty = - DependencyProperty.Register("ItemHeight", typeof(double), - typeof(GalleryPanel), new UIPropertyMetadata(double.NaN)); - - #endregion - - #region Filter - - /// - /// Gets or sets groups names separated by comma which must be shown - /// - public string Filter - { - get { return (string)this.GetValue(FilterProperty); } - set { this.SetValue(FilterProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Filter. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty FilterProperty = - DependencyProperty.Register("Filter", typeof(string), - typeof(GalleryPanel), new UIPropertyMetadata(null, OnFilterChanged)); - - private static void OnFilterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var galleryPanel = (GalleryPanel)d; - galleryPanel.Invalidate(); - } - - #endregion - - #region MinItemsInRow - - /// - /// Gets or sets maximum items quantity in row - /// - public int MinItemsInRow - { - get { return (int)this.GetValue(MinItemsInRowProperty); } - set { this.SetValue(MinItemsInRowProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for ItemsInRow. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty MinItemsInRowProperty = - DependencyProperty.Register("MinItemsInRow", typeof(int), - typeof(GalleryPanel), new UIPropertyMetadata((int)1)); - - #endregion - - #region MaxItemsInRow - - /// - /// Gets or sets maximum items quantity in row - /// - public int MaxItemsInRow - { - get { return (int)this.GetValue(MaxItemsInRowProperty); } - set { this.SetValue(MaxItemsInRowProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for ItemsInRow. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty MaxItemsInRowProperty = - DependencyProperty.Register("MaxItemsInRow", typeof(int), - typeof(GalleryPanel), new UIPropertyMetadata(int.MaxValue)); - - #endregion - - #endregion - - #region Initialization - - /// - /// Default constructor - /// - public GalleryPanel() - { - this.visualCollection = new VisualCollection(this); - } - - #endregion - - #region Visual Tree - - readonly VisualCollection visualCollection; - - /// - /// Gets the number of visual child elements within this element. - /// - protected override int VisualChildrenCount - { - get - { - return base.VisualChildrenCount + this.visualCollection.Count; - } - } - - /// - /// Overrides System.Windows.Media.Visual.GetVisualChild(System.Int32), - /// and returns a child at the specified index from a collection of child elements. - /// - /// The zero-based index of the requested - /// child element in the collection - /// The requested child element. This should not return null; - /// if the provided index is out of range, an exception is thrown - protected override Visual GetVisualChild(int index) - { - if (index < base.VisualChildrenCount) - { - return base.GetVisualChild(index); - } - - return this.visualCollection[index - base.VisualChildrenCount]; - } - - #endregion - - #region GetActualMinWidth - - /// - /// Updates MinWidth and MaxWidth of the gallery panel (based on MinItemsInRow and MaxItemsInRow) - /// - public void UpdateMinAndMaxWidth() - { - // Calculate actual min width - double actualMinWidth = 0; - var actualMaxWidth = double.PositiveInfinity; - - foreach (var galleryGroupContainer in this.galleryGroupContainers) - { - var backupMinItemsInRow = galleryGroupContainer.MinItemsInRow; - var backupMaxItemsInRow = galleryGroupContainer.MaxItemsInRow; - galleryGroupContainer.MinItemsInRow = this.MinItemsInRow; - galleryGroupContainer.MaxItemsInRow = this.MaxItemsInRow; - - InvalidateMeasureRecursive(galleryGroupContainer); - galleryGroupContainer.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); - - galleryGroupContainer.InvalidateMeasure(); - - actualMinWidth = Math.Max(actualMinWidth, galleryGroupContainer.MinWidth); - actualMaxWidth = Math.Min(actualMaxWidth, galleryGroupContainer.MaxWidth); - - galleryGroupContainer.MinItemsInRow = backupMinItemsInRow; - galleryGroupContainer.MaxItemsInRow = backupMaxItemsInRow; - } - - this.MinWidth = actualMinWidth; - this.MaxWidth = actualMaxWidth; - } - - private static void InvalidateMeasureRecursive(UIElement visual) - { - visual.InvalidateMeasure(); - - for (var i = 0; i < VisualTreeHelper.GetChildrenCount(visual); i++) - { - var element = VisualTreeHelper.GetChild(visual, i) as UIElement; - if (element != null) - { - InvalidateMeasureRecursive(element); - } - } - } - - #endregion - - #region GetItemSize - - /// - /// Determinates item's size (return Size.Empty in case of it is not possible) - /// - /// - public Size GetItemSize() - { - foreach (var galleryGroupContainer in this.galleryGroupContainers) - { - var size = galleryGroupContainer.GetItemSize(); - if (size.IsEmpty == false) - { - return size; - } - } - - return Size.Empty; - } - - #endregion - - #region Refresh - - private void Invalidate() - { - if (this.haveToBeRefreshed) - { - return; - } - - this.haveToBeRefreshed = true; - this.Dispatcher.BeginInvoke((Action)this.RefreshDispatchered, DispatcherPriority.Send); - } - - private void RefreshDispatchered() - { - if (this.haveToBeRefreshed == false) - { - return; - } - - this.Refresh(); - this.haveToBeRefreshed = false; - } - - private void Refresh() - { - // Clear currently used group containers - // and supply with new generated ones - foreach (var galleryGroupContainer in this.galleryGroupContainers) - { - BindingOperations.ClearAllBindings(galleryGroupContainer); - //RemoveVisualChild(galleryGroupContainer); - this.visualCollection.Remove(galleryGroupContainer); - } - - this.galleryGroupContainers.Clear(); - - // Gets filters - var filter = this.Filter == null ? null : this.Filter.Split(','); - - var dictionary = new Dictionary(); - - foreach(UIElement item in this.InternalChildren) - { - if (item == null) - { - continue; - } - - // Resolve group name - string propertyValue = null; - - if (this.GroupByAdvanced == null) - { - propertyValue = (this.ItemContainerGenerator == null) - ? this.GetPropertyValueAsString(item) - : this.GetPropertyValueAsString(this.ItemContainerGenerator.GetItemContainerGeneratorForPanel(this).ItemFromContainer(item)); - } - else - { - propertyValue = (this.ItemContainerGenerator == null) - ? this.GroupByAdvanced(item) - : this.GroupByAdvanced(this.ItemContainerGenerator.GetItemContainerGeneratorForPanel(this).ItemFromContainer(item)); - } - - if (propertyValue == null) - { - propertyValue = "Undefined"; - } - - // Make invisible if it is not in filter (or is not grouped) - if (this.IsGrouped == false - || (filter != null && filter.Contains(propertyValue) == false)) - { - item.Measure(new Size(0,0)); - item.Arrange(new Rect(0,0,0,0)); - } - - // Skip if it is not in filter - if (filter != null && - filter.Contains(propertyValue) == false) - { - continue; - } - - // To put all items in one group in case of IsGrouped = False - if (this.IsGrouped == false) - { - propertyValue = "Undefined"; - } - - if (dictionary.ContainsKey(propertyValue) == false) - { - var galleryGroupContainer = new GalleryGroupContainer - { - Header = propertyValue - }; - RibbonControl.Bind(this, galleryGroupContainer, "GroupStyle", GroupStyleProperty, BindingMode.OneWay); - RibbonControl.Bind(this, galleryGroupContainer, "Orientation", GalleryGroupContainer.OrientationProperty, BindingMode.OneWay); - RibbonControl.Bind(this, galleryGroupContainer, "ItemWidth", GalleryGroupContainer.ItemWidthProperty, BindingMode.OneWay); - RibbonControl.Bind(this, galleryGroupContainer, "ItemHeight", GalleryGroupContainer.ItemHeightProperty, BindingMode.OneWay); - RibbonControl.Bind(this, galleryGroupContainer, "MaxItemsInRow", GalleryGroupContainer.MaxItemsInRowProperty, BindingMode.OneWay); - RibbonControl.Bind(this, galleryGroupContainer, "MinItemsInRow", GalleryGroupContainer.MinItemsInRowProperty, BindingMode.OneWay); - dictionary.Add(propertyValue,galleryGroupContainer); - this.galleryGroupContainers.Add(galleryGroupContainer); - - this.visualCollection.Add(galleryGroupContainer); - } - - dictionary[propertyValue].Items.Add(new GalleryItemPlaceholder(item)); - } - - if ((this.IsGrouped == false || (this.GroupBy == null && this.GroupByAdvanced == null)) - && this.galleryGroupContainers.Count != 0) - { - // Make it without headers - this.galleryGroupContainers[0].IsHeadered = false; - } - - this.InvalidateMeasure(); - } - - #endregion - - #region Layout Overrides - - /// - /// When overridden in a derived class, measures the size in - /// layout required for child elements and determines a size - /// for the derived class. - /// - /// - /// The size that this element determines it needs during layout, - /// based on its calculations of child element sizes. - /// - /// The available size that this element can give - /// to child elements. Infinity can be specified as a value to indicate that - /// the element will size to whatever content is available. - protected override Size MeasureOverride(Size availableSize) - { - var baseSize = base.MeasureOverride(availableSize); - - if (this.galleryGroupContainers.Count == 0) - { - return baseSize; - } - - double width = 0; - double height = 0; - foreach (var child in this.galleryGroupContainers) - { - child.Measure(availableSize); - height += child.DesiredSize.Height; - width = Math.Max(width, child.DesiredSize.Width); - } - - return new Size(width, height); - } - - /// - /// When overridden in a derived class, positions child elements - /// and determines a size for a derived class. - /// - /// The actual size used. - /// The final area within the parent that this - /// element should use to arrange itself and its children. - protected override Size ArrangeOverride(Size finalSize) - { - var baseSize = base.ArrangeOverride(finalSize); - - if (this.galleryGroupContainers.Count == 0) - { - return baseSize; - } - - var finalRect = new Rect(finalSize); - - foreach (var item in this.galleryGroupContainers) - { - finalRect.Height = item.DesiredSize.Height; - finalRect.Width = Math.Max(finalSize.Width, item.DesiredSize.Width); - - // Arrange a container to arrange placeholders - item.Arrange(finalRect); - - finalRect.Y += item.DesiredSize.Height; - - // Now arrange our actual items using arranged size of placeholders - foreach (GalleryItemPlaceholder placeholder in item.Items) - { - var leftTop = placeholder.TranslatePoint(new Point(), this); - - placeholder.Target.Arrange(new Rect(leftTop.X, leftTop.Y, - placeholder.ArrangedSize.Width, - placeholder.ArrangedSize.Height)); - } - } - - return finalSize; - } - - #endregion - - #region Private Methods - - private string GetPropertyValueAsString(object item) - { - if (item == null || - this.GroupBy == null) - { - return "Undefined"; - } - - var property = item.GetType().GetProperty(this.GroupBy, BindingFlags.Public | BindingFlags.Instance); - if (property == null) - { - return "Undefined"; - } - - var result = property.GetValue(item, null); - if (result == null) - { - return "Undefined"; - } - return result.ToString(); - } - - #endregion - - /// - /// Gets an enumerator that can iterate the logical child elements of this element. - /// - /// - /// An . This property has no default value. - /// - protected override IEnumerator LogicalChildren - { - get - { - var count = this.VisualChildrenCount; - - for (var i = 0; i < count; i++) - { - yield return this.GetVisualChild(i); - } - } - } - - /// - /// Called when the collection that is associated with the for this changes. - /// - /// The that raised the event.Provides data for the event. - protected override void OnItemsChanged(object sender, ItemsChangedEventArgs args) - { - base.OnItemsChanged(sender, args); - - this.Dispatcher.BeginInvoke(DispatcherPriority.Background, (Action)(this.Refresh)); - } - } +namespace Fluent +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Linq; + using System.Reflection; + using System.Windows; + using System.Windows.Controls; + using System.Windows.Controls.Primitives; + using System.Windows.Data; + using System.Windows.Media; + using System.Windows.Threading; + + /// + /// Represents panel for Gallery, InRibbonGallery, ComboBox + /// with grouping and filtering capabilities + /// + public class GalleryPanel : VirtualizingStackPanel + { + #region Fields + + // Currently used group containers + private readonly List galleryGroupContainers = new List(); + + // Designate that gallery panel must be refreshed its groups + private bool haveToBeRefreshed; + + // Group name resolver + private Func groupByAdvanced; + + #endregion + + #region Properties + + #region IsGrouped + + /// + /// Gets or sets whether gallery panel shows groups + /// (Filter property still works as usual) + /// + public bool IsGrouped + { + get { return (bool)this.GetValue(IsGroupedProperty); } + set { this.SetValue(IsGroupedProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for IsGrouped. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IsGroupedProperty = + DependencyProperty.Register("IsGrouped", typeof(bool), typeof(GalleryPanel), + new UIPropertyMetadata(true, OnIsGroupedChanged)); + + private static void OnIsGroupedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var galleryPanel = (GalleryPanel)d; + galleryPanel.Invalidate(); + } + + #endregion + + #region GroupBy + + /// + /// Gets or sets property name to group items + /// + public string GroupBy + { + get { return (string)this.GetValue(GroupByProperty); } + set { this.SetValue(GroupByProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for GroupBy. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty GroupByProperty = + DependencyProperty.Register("GroupBy", typeof(string), typeof(GalleryPanel), + new UIPropertyMetadata(null, OnGroupByChanged)); + + private static void OnGroupByChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var galleryPanel = (GalleryPanel)d; + galleryPanel.Invalidate(); + } + + #endregion + + #region GroupByAdvanced + + /// + /// Gets or sets custom user method to group items. + /// If this property is not null, GroupBy property is ignored + /// + public Func GroupByAdvanced + { + get { return this.groupByAdvanced; } + set + { + this.groupByAdvanced = value; + this.Invalidate(); + } + } + + #endregion + + #region GroupStyle + + /// + /// Gets or sets group style + /// + public Style GroupStyle + { + get { return (Style)this.GetValue(GroupStyleProperty); } + set { this.SetValue(GroupStyleProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for GroupHeaderStyle. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty GroupStyleProperty = + DependencyProperty.Register("GroupHeaderStyle", typeof(Style), + typeof(GalleryPanel), new UIPropertyMetadata(null)); + + #endregion + + #region ItemWidth + + /// + /// Gets or sets a value that specifies the width of + /// all items that are contained within + /// + public double ItemWidth + { + get { return (double)this.GetValue(ItemWidthProperty); } + set { this.SetValue(ItemWidthProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for ItemWidth. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty ItemWidthProperty = + DependencyProperty.Register("ItemWidth", typeof(double), + typeof(GalleryPanel), new UIPropertyMetadata(double.NaN)); + + #endregion + + #region ItemHeight + + /// + /// Gets or sets a value that specifies the height of + /// all items that are contained within + /// + public double ItemHeight + { + get { return (double)this.GetValue(ItemHeightProperty); } + set { this.SetValue(ItemHeightProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for ItemHeight. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty ItemHeightProperty = + DependencyProperty.Register("ItemHeight", typeof(double), + typeof(GalleryPanel), new UIPropertyMetadata(double.NaN)); + + #endregion + + #region Filter + + /// + /// Gets or sets groups names separated by comma which must be shown + /// + public string Filter + { + get { return (string)this.GetValue(FilterProperty); } + set { this.SetValue(FilterProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Filter. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty FilterProperty = + DependencyProperty.Register("Filter", typeof(string), + typeof(GalleryPanel), new UIPropertyMetadata(null, OnFilterChanged)); + + private static void OnFilterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var galleryPanel = (GalleryPanel)d; + galleryPanel.Invalidate(); + } + + #endregion + + #region MinItemsInRow + + /// + /// Gets or sets maximum items quantity in row + /// + public int MinItemsInRow + { + get { return (int)this.GetValue(MinItemsInRowProperty); } + set { this.SetValue(MinItemsInRowProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for ItemsInRow. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty MinItemsInRowProperty = + DependencyProperty.Register("MinItemsInRow", typeof(int), + typeof(GalleryPanel), new UIPropertyMetadata((int)1)); + + #endregion + + #region MaxItemsInRow + + /// + /// Gets or sets maximum items quantity in row + /// + public int MaxItemsInRow + { + get { return (int)this.GetValue(MaxItemsInRowProperty); } + set { this.SetValue(MaxItemsInRowProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for ItemsInRow. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty MaxItemsInRowProperty = + DependencyProperty.Register("MaxItemsInRow", typeof(int), + typeof(GalleryPanel), new UIPropertyMetadata(int.MaxValue)); + + #endregion + + #endregion + + #region Initialization + + /// + /// Default constructor + /// + public GalleryPanel() + { + this.visualCollection = new VisualCollection(this); + } + + #endregion + + #region Visual Tree + + readonly VisualCollection visualCollection; + + /// + /// Gets the number of visual child elements within this element. + /// + protected override int VisualChildrenCount + { + get + { + return base.VisualChildrenCount + this.visualCollection.Count; + } + } + + /// + /// Overrides System.Windows.Media.Visual.GetVisualChild(System.Int32), + /// and returns a child at the specified index from a collection of child elements. + /// + /// The zero-based index of the requested + /// child element in the collection + /// The requested child element. This should not return null; + /// if the provided index is out of range, an exception is thrown + protected override Visual GetVisualChild(int index) + { + if (index < base.VisualChildrenCount) + { + return base.GetVisualChild(index); + } + + return this.visualCollection[index - base.VisualChildrenCount]; + } + + #endregion + + #region GetActualMinWidth + + /// + /// Updates MinWidth and MaxWidth of the gallery panel (based on MinItemsInRow and MaxItemsInRow) + /// + public void UpdateMinAndMaxWidth() + { + // Calculate actual min width + double actualMinWidth = 0; + var actualMaxWidth = double.PositiveInfinity; + + foreach (var galleryGroupContainer in this.galleryGroupContainers) + { + var backupMinItemsInRow = galleryGroupContainer.MinItemsInRow; + var backupMaxItemsInRow = galleryGroupContainer.MaxItemsInRow; + galleryGroupContainer.MinItemsInRow = this.MinItemsInRow; + galleryGroupContainer.MaxItemsInRow = this.MaxItemsInRow; + + InvalidateMeasureRecursive(galleryGroupContainer); + galleryGroupContainer.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); + + galleryGroupContainer.InvalidateMeasure(); + + actualMinWidth = Math.Max(actualMinWidth, galleryGroupContainer.MinWidth); + actualMaxWidth = Math.Min(actualMaxWidth, galleryGroupContainer.MaxWidth); + + galleryGroupContainer.MinItemsInRow = backupMinItemsInRow; + galleryGroupContainer.MaxItemsInRow = backupMaxItemsInRow; + } + + this.MinWidth = actualMinWidth; + this.MaxWidth = actualMaxWidth; + } + + private static void InvalidateMeasureRecursive(UIElement visual) + { + visual.InvalidateMeasure(); + + for (var i = 0; i < VisualTreeHelper.GetChildrenCount(visual); i++) + { + var element = VisualTreeHelper.GetChild(visual, i) as UIElement; + if (element != null) + { + InvalidateMeasureRecursive(element); + } + } + } + + #endregion + + #region GetItemSize + + /// + /// Determinates item's size (return Size.Empty in case of it is not possible) + /// + /// + public Size GetItemSize() + { + foreach (var galleryGroupContainer in this.galleryGroupContainers) + { + var size = galleryGroupContainer.GetItemSize(); + if (size.IsEmpty == false) + { + return size; + } + } + + return Size.Empty; + } + + #endregion + + #region Refresh + + private void Invalidate() + { + if (this.haveToBeRefreshed) + { + return; + } + + this.haveToBeRefreshed = true; + this.Dispatcher.BeginInvoke((Action)this.RefreshDispatchered, DispatcherPriority.Send); + } + + private void RefreshDispatchered() + { + if (this.haveToBeRefreshed == false) + { + return; + } + + this.Refresh(); + this.haveToBeRefreshed = false; + } + + private void Refresh() + { + // Clear currently used group containers + // and supply with new generated ones + foreach (var galleryGroupContainer in this.galleryGroupContainers) + { + BindingOperations.ClearAllBindings(galleryGroupContainer); + //RemoveVisualChild(galleryGroupContainer); + this.visualCollection.Remove(galleryGroupContainer); + } + + this.galleryGroupContainers.Clear(); + + // Gets filters + var filter = this.Filter == null ? null : this.Filter.Split(','); + + var dictionary = new Dictionary(); + + foreach(UIElement item in this.InternalChildren) + { + if (item == null) + { + continue; + } + + // Resolve group name + string propertyValue = null; + + if (this.GroupByAdvanced == null) + { + propertyValue = (this.ItemContainerGenerator == null) + ? this.GetPropertyValueAsString(item) + : this.GetPropertyValueAsString(this.ItemContainerGenerator.GetItemContainerGeneratorForPanel(this).ItemFromContainer(item)); + } + else + { + propertyValue = (this.ItemContainerGenerator == null) + ? this.GroupByAdvanced(item) + : this.GroupByAdvanced(this.ItemContainerGenerator.GetItemContainerGeneratorForPanel(this).ItemFromContainer(item)); + } + + if (propertyValue == null) + { + propertyValue = "Undefined"; + } + + // Make invisible if it is not in filter (or is not grouped) + if (this.IsGrouped == false + || (filter != null && filter.Contains(propertyValue) == false)) + { + item.Measure(new Size(0,0)); + item.Arrange(new Rect(0,0,0,0)); + } + + // Skip if it is not in filter + if (filter != null && + filter.Contains(propertyValue) == false) + { + continue; + } + + // To put all items in one group in case of IsGrouped = False + if (this.IsGrouped == false) + { + propertyValue = "Undefined"; + } + + if (dictionary.ContainsKey(propertyValue) == false) + { + var galleryGroupContainer = new GalleryGroupContainer + { + Header = propertyValue + }; + RibbonControl.Bind(this, galleryGroupContainer, "GroupStyle", GroupStyleProperty, BindingMode.OneWay); + RibbonControl.Bind(this, galleryGroupContainer, "Orientation", GalleryGroupContainer.OrientationProperty, BindingMode.OneWay); + RibbonControl.Bind(this, galleryGroupContainer, "ItemWidth", GalleryGroupContainer.ItemWidthProperty, BindingMode.OneWay); + RibbonControl.Bind(this, galleryGroupContainer, "ItemHeight", GalleryGroupContainer.ItemHeightProperty, BindingMode.OneWay); + RibbonControl.Bind(this, galleryGroupContainer, "MaxItemsInRow", GalleryGroupContainer.MaxItemsInRowProperty, BindingMode.OneWay); + RibbonControl.Bind(this, galleryGroupContainer, "MinItemsInRow", GalleryGroupContainer.MinItemsInRowProperty, BindingMode.OneWay); + dictionary.Add(propertyValue,galleryGroupContainer); + this.galleryGroupContainers.Add(galleryGroupContainer); + + this.visualCollection.Add(galleryGroupContainer); + } + + dictionary[propertyValue].Items.Add(new GalleryItemPlaceholder(item)); + } + + if ((this.IsGrouped == false || (this.GroupBy == null && this.GroupByAdvanced == null)) + && this.galleryGroupContainers.Count != 0) + { + // Make it without headers + this.galleryGroupContainers[0].IsHeadered = false; + } + + this.InvalidateMeasure(); + } + + #endregion + + #region Layout Overrides + + /// + /// When overridden in a derived class, measures the size in + /// layout required for child elements and determines a size + /// for the derived class. + /// + /// + /// The size that this element determines it needs during layout, + /// based on its calculations of child element sizes. + /// + /// The available size that this element can give + /// to child elements. Infinity can be specified as a value to indicate that + /// the element will size to whatever content is available. + protected override Size MeasureOverride(Size availableSize) + { + var baseSize = base.MeasureOverride(availableSize); + + if (this.galleryGroupContainers.Count == 0) + { + return baseSize; + } + + double width = 0; + double height = 0; + foreach (var child in this.galleryGroupContainers) + { + child.Measure(availableSize); + height += child.DesiredSize.Height; + width = Math.Max(width, child.DesiredSize.Width); + } + + return new Size(width, height); + } + + /// + /// When overridden in a derived class, positions child elements + /// and determines a size for a derived class. + /// + /// The actual size used. + /// The final area within the parent that this + /// element should use to arrange itself and its children. + protected override Size ArrangeOverride(Size finalSize) + { + var baseSize = base.ArrangeOverride(finalSize); + + if (this.galleryGroupContainers.Count == 0) + { + return baseSize; + } + + var finalRect = new Rect(finalSize); + + foreach (var item in this.galleryGroupContainers) + { + finalRect.Height = item.DesiredSize.Height; + finalRect.Width = Math.Max(finalSize.Width, item.DesiredSize.Width); + + // Arrange a container to arrange placeholders + item.Arrange(finalRect); + + finalRect.Y += item.DesiredSize.Height; + + // Now arrange our actual items using arranged size of placeholders + foreach (GalleryItemPlaceholder placeholder in item.Items) + { + var leftTop = placeholder.TranslatePoint(new Point(), this); + + placeholder.Target.Arrange(new Rect(leftTop.X, leftTop.Y, + placeholder.ArrangedSize.Width, + placeholder.ArrangedSize.Height)); + } + } + + return finalSize; + } + + #endregion + + #region Private Methods + + private string GetPropertyValueAsString(object item) + { + if (item == null || + this.GroupBy == null) + { + return "Undefined"; + } + + var property = item.GetType().GetProperty(this.GroupBy, BindingFlags.Public | BindingFlags.Instance); + if (property == null) + { + return "Undefined"; + } + + var result = property.GetValue(item, null); + if (result == null) + { + return "Undefined"; + } + return result.ToString(); + } + + #endregion + + /// + /// Gets an enumerator that can iterate the logical child elements of this element. + /// + /// + /// An . This property has no default value. + /// + protected override IEnumerator LogicalChildren + { + get + { + var count = this.VisualChildrenCount; + + for (var i = 0; i < count; i++) + { + yield return this.GetVisualChild(i); + } + } + } + + /// + /// Called when the collection that is associated with the for this changes. + /// + /// The that raised the event.Provides data for the event. + protected override void OnItemsChanged(object sender, ItemsChangedEventArgs args) + { + base.OnItemsChanged(sender, args); + + this.Dispatcher.BeginInvoke(DispatcherPriority.Background, (Action)(this.Refresh)); + } + } } \ No newline at end of file diff --git a/Fluent/Controls/GroupSeparatorMenuItem.cs b/Fluent.Ribbon/Controls/GroupSeparatorMenuItem.cs similarity index 97% rename from Fluent/Controls/GroupSeparatorMenuItem.cs rename to Fluent.Ribbon/Controls/GroupSeparatorMenuItem.cs index 355db92f4..42b6bef4e 100644 --- a/Fluent/Controls/GroupSeparatorMenuItem.cs +++ b/Fluent.Ribbon/Controls/GroupSeparatorMenuItem.cs @@ -1,40 +1,40 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.Windows; -using System.Windows.Markup; - -namespace Fluent -{ - /// - /// Represents group separator menu item - /// - [ContentProperty("Header")] - public class GroupSeparatorMenuItem: MenuItem - { - [SuppressMessage("Microsoft.Performance", "CA1810")] - static GroupSeparatorMenuItem() - { - var type = typeof (GroupSeparatorMenuItem); - DefaultStyleKeyProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(type)); - StyleProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceStyle))); - IsEnabledProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(false, null, CoerceIsEnabledAndTabStop)); - IsTabStopProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(false, null, CoerceIsEnabledAndTabStop)); - } - - private static object CoerceIsEnabledAndTabStop(DependencyObject d, object basevalue) - { - return false; - } - - // Coerce object style - private static object OnCoerceStyle(DependencyObject d, object basevalue) - { - if (basevalue == null) - { - basevalue = (d as FrameworkElement).TryFindResource(typeof(GroupSeparatorMenuItem)); - } - - return basevalue; - } - } +using System; +using System.Diagnostics.CodeAnalysis; +using System.Windows; +using System.Windows.Markup; + +namespace Fluent +{ + /// + /// Represents group separator menu item + /// + [ContentProperty("Header")] + public class GroupSeparatorMenuItem: MenuItem + { + [SuppressMessage("Microsoft.Performance", "CA1810")] + static GroupSeparatorMenuItem() + { + var type = typeof (GroupSeparatorMenuItem); + DefaultStyleKeyProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(type)); + StyleProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceStyle))); + IsEnabledProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(false, null, CoerceIsEnabledAndTabStop)); + IsTabStopProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(false, null, CoerceIsEnabledAndTabStop)); + } + + private static object CoerceIsEnabledAndTabStop(DependencyObject d, object basevalue) + { + return false; + } + + // Coerce object style + private static object OnCoerceStyle(DependencyObject d, object basevalue) + { + if (basevalue == null) + { + basevalue = (d as FrameworkElement).TryFindResource(typeof(GroupSeparatorMenuItem)); + } + + return basevalue; + } + } } \ No newline at end of file diff --git a/Fluent/Controls/InRibbonGallery.cs b/Fluent.Ribbon/Controls/InRibbonGallery.cs similarity index 97% rename from Fluent/Controls/InRibbonGallery.cs rename to Fluent.Ribbon/Controls/InRibbonGallery.cs index 148999470..817d3c997 100644 --- a/Fluent/Controls/InRibbonGallery.cs +++ b/Fluent.Ribbon/Controls/InRibbonGallery.cs @@ -1,1517 +1,1517 @@ -namespace Fluent -{ - using System; - using System.Collections; - using System.Collections.ObjectModel; - using System.Collections.Specialized; - using System.Diagnostics; - using System.Diagnostics.CodeAnalysis; - using System.Linq; - using System.Threading; - using System.Windows; - using System.Windows.Controls; - using System.Windows.Controls.Primitives; - using System.Windows.Data; - using System.Windows.Input; - using System.Windows.Markup; - using System.Windows.Media; - using System.Windows.Media.Imaging; - using System.Windows.Threading; - using Fluent.Extensibility; - using Fluent.Internal; - - /// - /// Represents the In-Ribbon Gallery, a gallery-based control that exposes - /// a default subset of items directly in the Ribbon. Any remaining items - /// are displayed when a drop-down menu button is clicked - /// - [ContentProperty("Items")] - [SuppressMessage("Microsoft.Maintainability", "CA1506")] - public class InRibbonGallery : Selector, IScalableRibbonControl, IDropDownControl, IRibbonControl, IQuickAccessItemProvider, IRibbonSizeChangedSink - { - #region Fields - - private ObservableCollection filters; - - private ToggleButton expandButton; - private ToggleButton dropDownButton; - - private Panel menuPanel; - - // Freezed image (created during snapping) - private Image snappedImage; - - // Is visual currently snapped - private bool isSnapped; - - private Popup popup; - - // Thumb to resize in both directions - private Thumb resizeBothThumb; - - // Thumb to resize vertical - private Thumb resizeVerticalThumb; - - private DropDownButton groupsMenuButton; - - private GalleryPanel galleryPanel; - - private ContentControl controlPresenter; - private ContentControl popupControlPresenter; - - private ScrollViewer scrollViewer; - - // Needed to prevent drop down reopen - private bool canOpenDropDown = true; - - private IInputElement focusedElement; - - private bool isButtonClicked; - - private FrameworkElement layoutRoot; - - #endregion - - #region Properties - - #region Size - - /// - /// Gets or sets Size for the element. - /// - public RibbonControlSize Size - { - get { return (RibbonControlSize)this.GetValue(SizeProperty); } - set { this.SetValue(SizeProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Size. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty SizeProperty = RibbonProperties.SizeProperty.AddOwner(typeof(InRibbonGallery)); - - #endregion - - #region SizeDefinition - - /// - /// Gets or sets SizeDefinition for element. - /// - public RibbonControlSizeDefinition SizeDefinition - { - get { return (RibbonControlSizeDefinition)this.GetValue(SizeDefinitionProperty); } - set { this.SetValue(SizeDefinitionProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for SizeDefinition. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty SizeDefinitionProperty = RibbonProperties.SizeDefinitionProperty.AddOwner(typeof(InRibbonGallery)); - - #endregion - - #region KeyTip - - /// - /// Gets or sets KeyTip for element. - /// - public string KeyTip - { - get { return (string)this.GetValue(KeyTipProperty); } - set { this.SetValue(KeyTipProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Keys. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty KeyTipProperty = Fluent.KeyTip.KeysProperty.AddOwner(typeof(InRibbonGallery)); - - #endregion - - #region Header - - /// - /// Gets or sets element Text - /// - public object Header - { - get { return this.GetValue(HeaderProperty); } - set { this.SetValue(HeaderProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Header. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty HeaderProperty = RibbonControl.HeaderProperty.AddOwner(typeof(InRibbonGallery)); - - #endregion - - #region Icon - - /// - /// Gets or sets Icon for the element - /// - public object Icon - { - get { return this.GetValue(IconProperty); } - set { this.SetValue(IconProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Icon. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IconProperty = RibbonControl.IconProperty.AddOwner(typeof(InRibbonGallery)); - - #endregion - - #region MinItemsInDropDownRow - - /// - /// Min width of the Gallery - /// - public int MinItemsInDropDownRow - { - get { return (int)this.GetValue(MinItemsInDropDownRowProperty); } - set { this.SetValue(MinItemsInDropDownRowProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for MinItemsInDropDownRow. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty MinItemsInDropDownRowProperty = - DependencyProperty.Register("MinItemsInDropDownRow", typeof(int), typeof(InRibbonGallery), new UIPropertyMetadata(1)); - - #endregion - - #region MaxItemsInDropDownRow - - /// - /// Max width of the Gallery - /// - public int MaxItemsInDropDownRow - { - get { return (int)this.GetValue(MaxItemsInDropDownRowProperty); } - set { this.SetValue(MaxItemsInDropDownRowProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for MaxItemsInDropDownRow. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty MaxItemsInDropDownRowProperty = - DependencyProperty.Register("MaxItemsInDropDownRow", typeof(int), typeof(InRibbonGallery), new UIPropertyMetadata(int.MaxValue)); - - #endregion - - #region ItemWidth - - /// - /// Gets or sets item width - /// - public double ItemWidth - { - get { return (double)this.GetValue(ItemWidthProperty); } - set { this.SetValue(ItemWidthProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for ItemWidth. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty ItemWidthProperty = - DependencyProperty.Register("ItemWidth", typeof(double), typeof(InRibbonGallery), new UIPropertyMetadata(double.NaN)); - - /// - /// Gets or sets item height - /// - public double ItemHeight - { - get { return (double)this.GetValue(ItemHeightProperty); } - set { this.SetValue(ItemHeightProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for ItemHeight. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty ItemHeightProperty = - DependencyProperty.Register("ItemHeight", typeof(double), typeof(InRibbonGallery), new UIPropertyMetadata(double.NaN)); - - #endregion - - #region GroupBy - - /// - /// Gets or sets name of property which - /// will use to group items in the Gallery. - /// - public string GroupBy - { - get { return (string)this.GetValue(GroupByProperty); } - set { this.SetValue(GroupByProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for GroupBy. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty GroupByProperty = - DependencyProperty.Register("GroupBy", typeof(string), - typeof(InRibbonGallery), new UIPropertyMetadata(null)); - - #endregion - - #region Orientation - - /// - /// Gets or sets orientation of gallery - /// - public Orientation Orientation - { - get { return (Orientation)this.GetValue(OrientationProperty); } - set { this.SetValue(OrientationProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Orientation. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty OrientationProperty = - DependencyProperty.Register("Orientation", typeof(Orientation), typeof(InRibbonGallery), new UIPropertyMetadata(Orientation.Horizontal)); - - #endregion - - #region Filters - - /// - /// Gets collection of filters - /// - public ObservableCollection Filters - { - get - { - if (this.filters == null) - { - this.filters = new ObservableCollection(); - this.filters.CollectionChanged += this.OnFilterCollectionChanged; - } - return this.filters; - } - } - - // Handle toolbar items changes - private void OnFilterCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) - { - this.HasFilter = this.Filters.Count > 0; - this.InvalidateProperty(SelectedFilterProperty); - - switch (e.Action) - { - case NotifyCollectionChangedAction.Add: - foreach (var item in e.NewItems.OfType()) - { - if (this.groupsMenuButton != null) - { - GalleryGroupFilter filter = item; - MenuItem menuItem = new MenuItem(); - menuItem.Header = filter.Title; - menuItem.Tag = filter; - menuItem.IsDefinitive = false; - if (filter == this.SelectedFilter) menuItem.IsChecked = true; - menuItem.Click += this.OnFilterMenuItemClick; - this.groupsMenuButton.Items.Add(menuItem); - } - } - break; - case NotifyCollectionChangedAction.Remove: - foreach (var item in e.OldItems.OfType()) - { - if (this.groupsMenuButton != null) - { - this.groupsMenuButton.Items.Remove(this.GetFilterMenuItem(item)); - } - } - break; - - case NotifyCollectionChangedAction.Replace: - foreach (var item in e.OldItems.OfType()) - { - if (this.groupsMenuButton != null) - { - this.groupsMenuButton.Items.Remove(this.GetFilterMenuItem(item)); - } - } - foreach (var item in e.NewItems.OfType()) - { - if (this.groupsMenuButton != null) - { - GalleryGroupFilter filter = item; - MenuItem menuItem = new MenuItem(); - menuItem.Header = filter.Title; - menuItem.Tag = filter; - menuItem.IsDefinitive = false; - if (filter == this.SelectedFilter) menuItem.IsChecked = true; - menuItem.Click += this.OnFilterMenuItemClick; - this.groupsMenuButton.Items.Add(menuItem); - } - } - break; - case NotifyCollectionChangedAction.Reset: - - if (this.groupsMenuButton != null) - { - this.groupsMenuButton.Items.Clear(); - } - - break; - } - } - - /// - /// Gets or sets selected filter - /// - public GalleryGroupFilter SelectedFilter - { - get { return (GalleryGroupFilter)this.GetValue(SelectedFilterProperty); } - set { this.SetValue(SelectedFilterProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for SelectedFilter. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty SelectedFilterProperty = - DependencyProperty.Register("SelectedFilter", typeof(GalleryGroupFilter), - typeof(InRibbonGallery), new UIPropertyMetadata(null, OnFilterChanged, CoerceSelectedFilter)); - - // Coerce selected filter - static object CoerceSelectedFilter(DependencyObject d, object basevalue) - { - InRibbonGallery gallery = (InRibbonGallery)d; - if ((basevalue == null) && (gallery.Filters.Count > 0)) return gallery.Filters[0]; - return basevalue; - } - - // Handles filter property changed - static void OnFilterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - InRibbonGallery gallery = (InRibbonGallery)d; - GalleryGroupFilter oldFilter = e.OldValue as GalleryGroupFilter; - if (oldFilter != null) - { - System.Windows.Controls.MenuItem menuItem = gallery.GetFilterMenuItem(oldFilter); - if (menuItem != null) menuItem.IsChecked = false; - } - GalleryGroupFilter filter = e.NewValue as GalleryGroupFilter; - if (filter != null) - { - gallery.SelectedFilterTitle = filter.Title; - gallery.SelectedFilterGroups = filter.Groups; - System.Windows.Controls.MenuItem menuItem = gallery.GetFilterMenuItem(filter); - if (menuItem != null) menuItem.IsChecked = true; - } - else - { - gallery.SelectedFilterTitle = ""; - gallery.SelectedFilterGroups = null; - } - gallery.UpdateLayout(); - } - - /// - /// Gets selected filter title - /// - public string SelectedFilterTitle - { - get { return (string)this.GetValue(SelectedFilterTitleProperty); } - private set { this.SetValue(SelectedFilterTitlePropertyKey, value); } - } - - private static readonly DependencyPropertyKey SelectedFilterTitlePropertyKey = - DependencyProperty.RegisterReadOnly("SelectedFilterTitle", typeof(string), - typeof(InRibbonGallery), new UIPropertyMetadata(null)); - - /// - /// Using a DependencyProperty as the backing store for SelectedFilterTitle. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty SelectedFilterTitleProperty = SelectedFilterTitlePropertyKey.DependencyProperty; - - /// - /// Gets selected filter groups - /// - public string SelectedFilterGroups - { - get { return (string)this.GetValue(SelectedFilterGroupsProperty); } - private set { this.SetValue(SelectedFilterGroupsPropertyKey, value); } - } - - private static readonly DependencyPropertyKey SelectedFilterGroupsPropertyKey = - DependencyProperty.RegisterReadOnly("SelectedFilterGroups", typeof(string), - typeof(InRibbonGallery), new UIPropertyMetadata(null)); - - /// - /// Using a DependencyProperty as the backing store for SelectedFilterGroups. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty SelectedFilterGroupsProperty = SelectedFilterGroupsPropertyKey.DependencyProperty; - - /// - /// Gets whether gallery has selected filter - /// - public bool HasFilter - { - get { return (bool)this.GetValue(HasFilterProperty); } - private set { this.SetValue(HasFilterPropertyKey, value); } - } - - private static readonly DependencyPropertyKey HasFilterPropertyKey = DependencyProperty.RegisterReadOnly("HasFilter", typeof(bool), typeof(InRibbonGallery), new UIPropertyMetadata(false)); - - /// - /// Using a DependencyProperty as the backing store for HasFilter. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty HasFilterProperty = HasFilterPropertyKey.DependencyProperty; - - void OnFilterMenuItemClick(object sender, RoutedEventArgs e) - { - MenuItem senderItem = (MenuItem)sender; - MenuItem item = this.GetFilterMenuItem(this.SelectedFilter); - item.IsChecked = false; - senderItem.IsChecked = true; - this.SelectedFilter = senderItem.Tag as GalleryGroupFilter; - this.groupsMenuButton.IsDropDownOpen = false; - e.Handled = true; - } - - MenuItem GetFilterMenuItem(GalleryGroupFilter filter) - { - if (filter == null) return null; - if (this.groupsMenuButton == null) return null; - return this.groupsMenuButton.Items.Cast().FirstOrDefault(item => (item != null) && (item.Header.ToString() == filter.Title)); - } - - #endregion - - #region Selectable - - /// - /// Gets or sets whether gallery items can be selected - /// - public bool Selectable - { - get { return (bool)this.GetValue(SelectableProperty); } - set { this.SetValue(SelectableProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Selectable. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty SelectableProperty = - DependencyProperty.Register("Selectable", typeof(bool), - typeof(InRibbonGallery), new UIPropertyMetadata(true, OnSelectableChanged)); - - private static void OnSelectableChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - d.CoerceValue(SelectedItemProperty); - } - - #endregion - - #region IsDropDownOpen - - /// - /// Gets drop down popup - /// - public Popup DropDownPopup - { - get { return this.popup; } - } - - /// - /// Gets a value indicating whether context menu is opened - /// - public bool IsContextMenuOpened { get; set; } - - /// - /// Gets or sets whether popup is opened - /// - public bool IsDropDownOpen - { - get { return (bool)this.GetValue(IsDropDownOpenProperty); } - set { this.SetValue(IsDropDownOpenProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for IsOpen. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IsDropDownOpenProperty = - DependencyProperty.Register("IsDropDownOpen", typeof(bool), - typeof(InRibbonGallery), new UIPropertyMetadata(false)); - - #endregion - - #region ResizeMode - - /// - /// Gets or sets context menu resize mode - /// - public ContextMenuResizeMode ResizeMode - { - get { return (ContextMenuResizeMode)this.GetValue(ResizeModeProperty); } - set { this.SetValue(ResizeModeProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for ResizeMode. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty ResizeModeProperty = - DependencyProperty.Register("ResizeMode", typeof(ContextMenuResizeMode), typeof(InRibbonGallery), new UIPropertyMetadata(ContextMenuResizeMode.None)); - - #endregion - - #region CanCollapseToButton - - /// - /// Gets or sets whether InRibbonGallery - /// - public bool CanCollapseToButton - { - get { return (bool)this.GetValue(CanCollapseToButtonProperty); } - set { this.SetValue(CanCollapseToButtonProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for CanCollapseToButton. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty CanCollapseToButtonProperty = - DependencyProperty.Register("CanCollapseToButton", typeof(bool), typeof(InRibbonGallery), new UIPropertyMetadata(true)); - - #endregion - - #region IsCollapsed - - /// - /// Gets whether InRibbonGallery is collapsed to button - /// - public bool IsCollapsed - { - get { return (bool)this.GetValue(IsCollapsedProperty); } - set { this.SetValue(IsCollapsedProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for IsCollapsed. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IsCollapsedProperty = - DependencyProperty.Register("IsCollapsed", typeof(bool), typeof(InRibbonGallery), new UIPropertyMetadata(false)); - - #endregion - - #region LargeIcon - - /// - /// Button large icon - /// - public ImageSource LargeIcon - { - get { return (ImageSource)this.GetValue(LargeIconProperty); } - set { this.SetValue(LargeIconProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for SmallIcon. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty LargeIconProperty = - DependencyProperty.Register("LargeIcon", typeof(ImageSource), typeof(InRibbonGallery), new UIPropertyMetadata(null)); - - #endregion - - #region Snapping - - /// - /// Snaps / Unsnaps the Visual - /// (remove visuals and substitute with freezed image) - /// - public bool IsSnapped - { - get - { - return this.isSnapped; - } - set - { - if (value == this.isSnapped) return; - if (this.IsCollapsed) return; - - if (!this.IsVisible) return; - - if ((value) && (((int)this.ActualWidth > 0) && ((int)this.ActualHeight > 0))) - { - - // Render the freezed image - RenderOptions.SetBitmapScalingMode(this.snappedImage, BitmapScalingMode.NearestNeighbor); - RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap((int)this.galleryPanel.ActualWidth, - (int)this.galleryPanel.ActualHeight, 96, 96, - PixelFormats.Pbgra32); - renderTargetBitmap.Render(this.galleryPanel); - this.snappedImage.Source = renderTargetBitmap; - this.snappedImage.FlowDirection = this.FlowDirection; - this.snappedImage.Width = this.galleryPanel.ActualWidth; - this.snappedImage.Height = this.galleryPanel.ActualHeight; - this.snappedImage.Visibility = Visibility.Visible; - this.isSnapped = value; - } - else - { - this.snappedImage.Visibility = Visibility.Collapsed; - this.isSnapped = value; - this.InvalidateVisual(); - } - - this.InvalidateVisual(); - } - } - - #endregion - - #region Menu - - /// - /// Gets or sets menu to show in combo box bottom - /// - public RibbonMenu Menu - { - get { return (RibbonMenu)this.GetValue(MenuProperty); } - set { this.SetValue(MenuProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Menu. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty MenuProperty = - DependencyProperty.Register("Menu", typeof(RibbonMenu), typeof(InRibbonGallery), new UIPropertyMetadata(null)); - - #endregion - - #region Min/Max Sizes - - /// - /// Gets or sets max count of items in row - /// - public int MaxItemsInRow - { - get { return (int)this.GetValue(MaxItemsInRowProperty); } - set { this.SetValue(MaxItemsInRowProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for MaxItemsInRow. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty MaxItemsInRowProperty = - DependencyProperty.Register("MaxItemsInRow", typeof(int), typeof(InRibbonGallery), new UIPropertyMetadata(8, OnMaxItemsInRowChanged)); - - /// - /// Gets or sets min count of items in row - /// - public int MinItemsInRow - { - get { return (int)this.GetValue(MinItemsInRowProperty); } - set { this.SetValue(MinItemsInRowProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for MaxItemsInRow. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty MinItemsInRowProperty = - DependencyProperty.Register("MinItemsInRow", typeof(int), typeof(InRibbonGallery), new UIPropertyMetadata(1)); - - private static void OnMaxItemsInRowChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - InRibbonGallery gal = d as InRibbonGallery; - int minItemsInRow = (int)e.NewValue; - if (!gal.IsDropDownOpen && (gal.galleryPanel != null) && (gal.galleryPanel.MinItemsInRow < minItemsInRow)) - { - gal.galleryPanel.MinItemsInRow = minItemsInRow; - gal.galleryPanel.MaxItemsInRow = minItemsInRow; - } - } - - #endregion - - #region MaxDropDownHeight - - /// - /// Get or sets max height of drop down popup - /// - public double MaxDropDownHeight - { - get { return (double)this.GetValue(MaxDropDownHeightProperty); } - set { this.SetValue(MaxDropDownHeightProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for MaxDropDownHeight. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty MaxDropDownHeightProperty = - DependencyProperty.Register("MaxDropDownHeight", typeof(double), typeof(InRibbonGallery), new UIPropertyMetadata(SystemParameters.PrimaryScreenHeight / 3.0)); - - #endregion - - #region MaxDropDownWidth - - /// - /// Get or sets max width of drop down popup - /// - public double MaxDropDownWidth - { - get { return (double)this.GetValue(MaxDropDownWidthProperty); } - set { this.SetValue(MaxDropDownWidthProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for MaxDropDownWidth. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty MaxDropDownWidthProperty = - DependencyProperty.Register("MaxDropDownWidth", typeof(double), typeof(InRibbonGallery), new UIPropertyMetadata(SystemParameters.PrimaryScreenWidth / 3.0)); - - #endregion - - #region DropDownHeight - - /// - /// Gets or sets initial dropdown height - /// - public double DropDownHeight - { - get { return (double)this.GetValue(DropDownHeightProperty); } - set { this.SetValue(DropDownHeightProperty, value); } - } - - /// - /// /Using a DependencyProperty as the backing store for DropDownHeight. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty DropDownHeightProperty = - DependencyProperty.Register("DropDownHeight", typeof(double), typeof(InRibbonGallery), new UIPropertyMetadata(double.NaN)); - - #endregion - - #region DropDownWidth - - /// - /// Gets or sets initial dropdown width - /// - public double DropDownWidth - { - get { return (double)this.GetValue(DropDownWidthProperty); } - set { this.SetValue(DropDownWidthProperty, value); } - } - - /// - /// /Using a DependencyProperty as the backing store for DropDownWidth. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty DropDownWidthProperty = - DependencyProperty.Register("DropDownWidth", typeof(double), typeof(InRibbonGallery), new UIPropertyMetadata(double.NaN)); - - #endregion - - #region ShowPopupOnTop - - /// - /// Gets a value indicating whether popup is shown on top; - /// - public bool ShowPopupOnTop - { - get { return (bool)this.GetValue(ShowPopupOnTopProperty); } - private set { this.SetValue(ShowPopupOnTopPropertyKey, value); } - } - - // - private static readonly DependencyPropertyKey ShowPopupOnTopPropertyKey = DependencyProperty.RegisterReadOnly("ShowPopupOnTop", typeof(bool), typeof(InRibbonGallery), new UIPropertyMetadata(false)); - - /// - /// Using a DependencyProperty as the backing store for ShowPopupOnTop. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty ShowPopupOnTopProperty = ShowPopupOnTopPropertyKey.DependencyProperty; - - #endregion - - #endregion - - #region Events - - /// - /// Occurs when control is scaled - /// - public event EventHandler Scaled; - - /// - /// Occurs when context menu is opened - /// - public event EventHandler DropDownOpened; - - /// - /// Occurs when context menu is closed - /// - public event EventHandler DropDownClosed; - - #endregion - - #region Constructors - - /// - /// Static constructor - /// - [SuppressMessage("Microsoft.Performance", "CA1810")] - static InRibbonGallery() - { - Type type = typeof(InRibbonGallery); - ToolTipService.Attach(type); - PopupService.Attach(type); - ContextMenuService.Attach(type); - DefaultStyleKeyProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(type)); - StyleProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceStyle))); - SelectedItemProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(null, CoerceSelectedItem)); - } - - // Coerce object style - static object OnCoerceStyle(DependencyObject d, object basevalue) - { - if (basevalue == null) - { - basevalue = (d as FrameworkElement).TryFindResource(typeof(InRibbonGallery)); - } - - return basevalue; - } - - // Coerce selected item - private static object CoerceSelectedItem(DependencyObject d, object basevalue) - { - InRibbonGallery gallery = (InRibbonGallery)d; - if (!gallery.Selectable) - { - GalleryItem galleryItem = (GalleryItem)gallery.ItemContainerGenerator.ContainerFromItem(basevalue); - if (basevalue != null && galleryItem != null) galleryItem.IsSelected = false; - return null; - } - return basevalue; - } - - /// - /// Default constructor - /// - public InRibbonGallery() - { - ContextMenuService.Coerce(this); - } - - #endregion - - #region Overrides - - /// - /// Handles key tip pressed - /// - public void OnKeyTipPressed() - { - this.IsDropDownOpen = true; - } - - /// - /// Handles back navigation with KeyTips - /// - public void OnKeyTipBack() - { - this.IsDropDownOpen = false; - } - - /// - /// Called when the selection changes. - /// - /// The event data. - protected override void OnSelectionChanged(SelectionChangedEventArgs e) - { - foreach (var item in e.RemovedItems) - { - GalleryItem itemContainer = (this.ItemContainerGenerator.ContainerFromItem(item) as GalleryItem); - if (itemContainer != null) itemContainer.IsSelected = false; - } - - foreach (var item in e.AddedItems) - { - GalleryItem itemContainer = (this.ItemContainerGenerator.ContainerFromItem(item) as GalleryItem); - if (itemContainer != null) itemContainer.IsSelected = true; - } - //if (IsDropDownOpen) IsDropDownOpen = false; - base.OnSelectionChanged(e); - } - - /// - /// When overridden in a derived class, is invoked whenever application - /// code or internal processes call ApplyTemplate - /// - public override void OnApplyTemplate() - { - this.layoutRoot = this.GetTemplateChild("PART_LayoutRoot") as FrameworkElement; - - if (this.expandButton != null) - this.expandButton.Click -= this.OnExpandClick; - this.expandButton = this.GetTemplateChild("PART_ExpandButton") as ToggleButton; - if (this.expandButton != null) - this.expandButton.Click += this.OnExpandClick; - - if (this.dropDownButton != null) - this.dropDownButton.Click -= this.OnDropDownClick; - this.dropDownButton = this.GetTemplateChild("PART_DropDownButton") as ToggleButton; - if (this.dropDownButton != null) - this.dropDownButton.Click += this.OnDropDownClick; - - if (this.popup != null) - { - this.popup.Opened -= this.OnDropDownOpened; - this.popup.Closed -= this.OnDropDownClosed; - - this.popup.PreviewMouseLeftButtonUp -= this.OnPopupPreviewMouseUp; - this.popup.PreviewMouseLeftButtonDown -= this.OnPopupPreviewMouseDown; - } - - this.popup = this.GetTemplateChild("PART_Popup") as Popup; - - if (this.popup != null) - { - this.popup.Opened += this.OnDropDownOpened; - this.popup.Closed += this.OnDropDownClosed; - - this.popup.PreviewMouseLeftButtonUp += this.OnPopupPreviewMouseUp; - this.popup.PreviewMouseLeftButtonDown += this.OnPopupPreviewMouseDown; - - KeyboardNavigation.SetControlTabNavigation(this.popup, KeyboardNavigationMode.Cycle); - KeyboardNavigation.SetDirectionalNavigation(this.popup, KeyboardNavigationMode.Cycle); - KeyboardNavigation.SetTabNavigation(this.popup, KeyboardNavigationMode.Cycle); - } - - if (this.resizeVerticalThumb != null) - { - this.resizeVerticalThumb.DragDelta -= this.OnResizeVerticalDelta; - } - this.resizeVerticalThumb = this.GetTemplateChild("PART_ResizeVerticalThumb") as Thumb; - if (this.resizeVerticalThumb != null) - { - this.resizeVerticalThumb.DragDelta += this.OnResizeVerticalDelta; - } - - if (this.resizeBothThumb != null) - { - this.resizeBothThumb.DragDelta -= this.OnResizeBothDelta; - } - this.resizeBothThumb = this.GetTemplateChild("PART_ResizeBothThumb") as Thumb; - if (this.resizeBothThumb != null) - { - this.resizeBothThumb.DragDelta += this.OnResizeBothDelta; - } - - this.menuPanel = this.GetTemplateChild("PART_MenuPanel") as Panel; - - if (this.groupsMenuButton != null) - this.groupsMenuButton.Items.Clear(); - this.groupsMenuButton = this.GetTemplateChild("PART_FilterDropDownButton") as DropDownButton; - if (this.groupsMenuButton != null) - { - for (int i = 0; i < this.Filters.Count; i++) - { - MenuItem item = new MenuItem(); - item.Header = this.Filters[i].Title; - item.Tag = this.Filters[i]; - item.IsDefinitive = false; - if (this.Filters[i] == this.SelectedFilter) item.IsChecked = true; - item.Click += this.OnFilterMenuItemClick; - this.groupsMenuButton.Items.Add(item); - } - } - - this.galleryPanel = this.GetTemplateChild("PART_GalleryPanel") as GalleryPanel; - - if (this.galleryPanel != null) - { - this.galleryPanel.MinItemsInRow = this.MaxItemsInRow; - this.galleryPanel.MaxItemsInRow = this.MaxItemsInRow; - } - - this.snappedImage = this.GetTemplateChild("PART_FakeImage") as Image; - - this.controlPresenter = this.GetTemplateChild("PART_ContentPresenter") as ContentControl; - this.popupControlPresenter = this.GetTemplateChild("PART_PopupContentPresenter") as ContentControl; - - this.scrollViewer = this.GetTemplateChild("PART_ScrollViewer") as ScrollViewer; - } - - private void OnPopupPreviewMouseUp(object sender, MouseButtonEventArgs e) - { - // Ignore mouse up when mouse donw is on expand button - if (this.isButtonClicked) - { - this.isButtonClicked = false; - e.Handled = true; - } - } - - private void OnPopupPreviewMouseDown(object sender, MouseButtonEventArgs e) - { - this.isButtonClicked = false; - } - - private void OnExpandClick(object sender, RoutedEventArgs e) - { - this.isButtonClicked = true; - } - - private void OnDropDownClick(object sender, RoutedEventArgs e) - { - if (this.canOpenDropDown) - this.IsDropDownOpen = true; - } - - // Handles drop down opened - void OnDropDownClosed(object sender, EventArgs e) - { - this.galleryPanel.Width = double.NaN; - this.galleryPanel.IsGrouped = false; - this.galleryPanel.MinItemsInRow = this.MinItemsInRow; - this.galleryPanel.MaxItemsInRow = this.MaxItemsInRow; - this.galleryPanel.UpdateMinAndMaxWidth(); - - this.popupControlPresenter.Content = null; - this.controlPresenter.Content = this.galleryPanel; - this.Dispatcher.BeginInvoke(DispatcherPriority.SystemIdle, (ThreadStart)(() => - { - if ((this.quickAccessGallery == null) || ((this.quickAccessGallery != null) && (!this.quickAccessGallery.IsDropDownOpen))) - { - this.IsSnapped = false; - } - })); - - //snappedImage.Visibility = Visibility.Collapsed; - if (this.DropDownClosed != null) - this.DropDownClosed(this, e); - if (Mouse.Captured == this) Mouse.Capture(null); - this.Dispatcher.BeginInvoke(DispatcherPriority.SystemIdle, (ThreadStart)(() => - { - GalleryItem selectedContainer = this.ItemContainerGenerator.ContainerFromItem(this.SelectedItem) as GalleryItem; - if (selectedContainer != null) selectedContainer.BringIntoView(); - - })); - this.dropDownButton.IsChecked = false; - this.canOpenDropDown = true; - } - - // Handles drop down closed - private void OnDropDownOpened(object sender, EventArgs e) - { - this.IsSnapped = true; - - this.controlPresenter.Content = null; - this.popupControlPresenter.Content = this.galleryPanel; - this.galleryPanel.Width = double.NaN; - this.scrollViewer.Height = double.NaN; - - if (this.DropDownOpened != null) - { - this.DropDownOpened(this, e); - } - - this.galleryPanel.MinItemsInRow = this.MinItemsInDropDownRow; - this.galleryPanel.MaxItemsInRow = this.MaxItemsInDropDownRow; - this.galleryPanel.UpdateMinAndMaxWidth(); - - this.galleryPanel.IsGrouped = true; - this.dropDownButton.IsChecked = true; - this.canOpenDropDown = false; - - Mouse.Capture(this, CaptureMode.SubTree); - - this.focusedElement = Keyboard.FocusedElement; - Debug.WriteLine("Focused element - " + this.focusedElement); - - if (this.focusedElement != null) - { - this.focusedElement.LostKeyboardFocus += this.OnFocusedElementLostKeyboardFocus; - this.focusedElement.PreviewKeyDown += this.OnFocusedElementPreviewKeyDown; - } - - //if (ResizeMode != ContextMenuResizeMode.None) - { - this.scrollViewer.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); - - var initialHeight = Math.Min(RibbonControl.GetControlWorkArea(this).Height, this.MaxDropDownHeight); - - if (!double.IsNaN(this.DropDownHeight)) - { - initialHeight = Math.Min(this.DropDownHeight, this.MaxDropDownHeight); - } - - var initialWidth = Math.Min(RibbonControl.GetControlWorkArea(this).Height, this.MaxDropDownWidth); - - if (!double.IsNaN(this.DropDownWidth)) - { - initialWidth = Math.Min(this.DropDownWidth, this.MaxDropDownWidth); - } - - double menuHeight = 0; - double menuWidth = 0; - - if (this.Menu != null) - { - this.Menu.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); - menuHeight = this.Menu.DesiredSize.Height; - menuWidth = this.Menu.DesiredSize.Width; - } - - if (this.scrollViewer.DesiredSize.Height > initialHeight) - { - this.scrollViewer.Height = initialHeight - menuHeight; - - if (this.scrollViewer.Height < this.galleryPanel.GetItemSize().Height) - { - this.scrollViewer.Height = this.galleryPanel.GetItemSize().Height; - } - } - - if (this.scrollViewer.DesiredSize.Width > initialWidth) - { - this.scrollViewer.Width = initialWidth - menuWidth; - - if (this.scrollViewer.Width < this.galleryPanel.GetItemSize().Width) - { - this.scrollViewer.Width = this.galleryPanel.GetItemSize().Width; - } - } - } - } - - /// - /// Handles size property changing - /// - /// Previous value - /// Current value - public void OnSizePropertyChanged(RibbonControlSize previous, RibbonControlSize current) - { - if (this.CanCollapseToButton) - { - if (current == RibbonControlSize.Large - && this.galleryPanel.MinItemsInRow > this.MinItemsInRow) - { - this.IsCollapsed = false; - } - else - { - this.IsCollapsed = true; - } - } - else - { - this.IsCollapsed = false; - } - } - - /// - /// Creates or identifies the element that is used to display the given item. - /// - /// The element that is used to display the given item. - protected override DependencyObject GetContainerForItemOverride() - { - return new GalleryItem(); - } - - /// - /// Determines if the specified item is (or is eligible to be) its own container. - /// - /// The item to check. - /// - protected override bool IsItemItsOwnContainerOverride(object item) - { - return item is GalleryItem; - } - - /// - /// Invoked when the event is received. - /// - /// Information about the event. - protected override void OnKeyDown(KeyEventArgs e) - { - if (e.Key == Key.Escape) - { - this.IsDropDownOpen = false; - } - - base.OnKeyDown(e); - } - - private void OnFocusedElementPreviewKeyDown(object sender, KeyEventArgs e) - { - if (e.Key == Key.Escape) - { - this.IsDropDownOpen = false; - } - } - - private void OnFocusedElementLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e) - { - this.focusedElement.LostKeyboardFocus -= this.OnFocusedElementLostKeyboardFocus; - this.focusedElement.PreviewKeyDown -= this.OnFocusedElementPreviewKeyDown; - } - - #endregion - - #region Private Methods - - // Handles resize both drag - private void OnResizeBothDelta(object sender, DragDeltaEventArgs e) - { - this.OnResizeVerticalDelta(sender, e); - - this.menuPanel.Width = double.NaN; - - if (double.IsNaN(this.galleryPanel.Width)) - { - this.galleryPanel.Width = this.galleryPanel.ActualWidth; - } - - this.galleryPanel.Width = Math.Max(this.layoutRoot.ActualWidth, this.galleryPanel.Width + e.HorizontalChange); - - } - - // Handles resize vertical drag - private void OnResizeVerticalDelta(object sender, DragDeltaEventArgs e) - { - if (double.IsNaN(this.scrollViewer.Height)) - { - this.scrollViewer.Height = this.scrollViewer.ActualHeight; - } - - this.scrollViewer.Height = Math.Max(this.layoutRoot.ActualHeight, Math.Min(Math.Max(this.galleryPanel.GetItemSize().Height, this.scrollViewer.Height + e.VerticalChange), this.MaxDropDownHeight)); - } - - #endregion - - #region QuickAccess - - /// - /// Gets control which represents shortcut item. - /// This item MUST be syncronized with the original - /// and send command to original one control. - /// - /// Control which represents shortcut item - public virtual FrameworkElement CreateQuickAccessItem() - { - var gallery = new InRibbonGallery(); - RibbonControl.BindQuickAccessItem(this, gallery); - RibbonControl.Bind(this, gallery, "GroupBy", GroupByProperty, BindingMode.OneWay); - RibbonControl.Bind(this, gallery, "ItemHeight", ItemHeightProperty, BindingMode.OneWay); - RibbonControl.Bind(this, gallery, "ItemWidth", ItemWidthProperty, BindingMode.OneWay); - RibbonControl.Bind(this, gallery, "ResizeMode", ResizeModeProperty, BindingMode.OneWay); - RibbonControl.Bind(this, gallery, "MinItemsInDropDownRow", MinItemsInDropDownRowProperty, BindingMode.OneWay); - RibbonControl.Bind(this, gallery, "MaxItemsInDropDownRow", MaxItemsInDropDownRowProperty, BindingMode.OneWay); - - RibbonControl.Bind(this, gallery, "DisplayMemberPath", DisplayMemberPathProperty, BindingMode.OneWay); - RibbonControl.Bind(this, gallery, "GroupStyleSelector", GroupStyleSelectorProperty, BindingMode.OneWay); - RibbonControl.Bind(this, gallery, "ItemContainerStyle", ItemContainerStyleProperty, BindingMode.OneWay); - RibbonControl.Bind(this, gallery, "ItemsPanel", ItemsPanelProperty, BindingMode.OneWay); - RibbonControl.Bind(this, gallery, "ItemStringFormat", ItemStringFormatProperty, BindingMode.OneWay); - RibbonControl.Bind(this, gallery, "ItemTemplate", ItemTemplateProperty, BindingMode.OneWay); - RibbonControl.Bind(this, gallery, "SelectedValuePath", SelectedValuePathProperty, BindingMode.OneWay); - RibbonControl.Bind(this, gallery, "MaxDropDownWidth", MaxDropDownWidthProperty, BindingMode.OneWay); - RibbonControl.Bind(this, gallery, "MaxDropDownHeight", MaxDropDownHeightProperty, BindingMode.OneWay); - - gallery.DropDownOpened += this.OnQuickAccessOpened; - if (this.DropDownClosed != null) gallery.DropDownClosed += this.DropDownClosed; - if (this.DropDownOpened != null) gallery.DropDownOpened += this.DropDownOpened; - - RibbonProperties.SetSize(gallery, RibbonControlSize.Small); - this.quickAccessGallery = gallery; - return gallery; - } - - private object selectedItem; - private InRibbonGallery quickAccessGallery; - - private void OnQuickAccessOpened(object sender, EventArgs e) - { - for (var i = 0; i < this.Filters.Count; i++) - { - this.quickAccessGallery.Filters.Add(this.Filters[i]); - } - - this.quickAccessGallery.SelectedFilter = this.SelectedFilter; - - this.quickAccessGallery.DropDownClosed += this.OnQuickAccessMenuClosedOrUnloaded; - this.quickAccessGallery.Unloaded += this.OnQuickAccessMenuClosedOrUnloaded; - - this.UpdateLayout(); - this.Dispatcher.BeginInvoke(DispatcherPriority.Render, ((Action)(this.Freeze))); - } - - private void OnQuickAccessMenuClosedOrUnloaded(object sender, EventArgs e) - { - this.quickAccessGallery.DropDownClosed -= this.OnQuickAccessMenuClosedOrUnloaded; - this.quickAccessGallery.Unloaded -= this.OnQuickAccessMenuClosedOrUnloaded; - - this.SelectedFilter = this.quickAccessGallery.SelectedFilter; - this.quickAccessGallery.Filters.Clear(); - this.Unfreeze(); - } - - private void Freeze() - { - this.IsSnapped = true; - this.selectedItem = this.SelectedItem; - this.SelectedItem = null; - - ItemsControlHelper.MoveItemsToDifferentControl(this, this.quickAccessGallery); - - this.quickAccessGallery.SelectedItem = this.selectedItem; - this.quickAccessGallery.Menu = this.Menu; - this.Menu = null; - //quickAccessGallery.IsSnapped = false; - } - - private void Unfreeze() - { - this.selectedItem = this.quickAccessGallery.SelectedItem; - //quickAccessGallery.IsSnapped = true; - this.quickAccessGallery.SelectedItem = null; - - ItemsControlHelper.MoveItemsToDifferentControl(this.quickAccessGallery, this); - - this.SelectedItem = this.selectedItem; - this.Menu = this.quickAccessGallery.Menu; - this.quickAccessGallery.Menu = null; - - if (!this.IsDropDownOpen) - { - if (this.controlPresenter != null) - { - this.controlPresenter.Content = null; - } - - if (this.popupControlPresenter != null) - { - this.popupControlPresenter.Content = this.galleryPanel; - } - - if (this.galleryPanel != null) - { - this.galleryPanel.IsGrouped = true; - this.galleryPanel.IsGrouped = false; - } - - if (this.popupControlPresenter != null) - { - this.popupControlPresenter.Content = null; - } - - if (this.controlPresenter != null) - { - this.controlPresenter.Content = this.galleryPanel; - } - } - - this.Dispatcher.BeginInvoke(DispatcherPriority.SystemIdle, ((ThreadStart)(() => - { - if (!this.IsDropDownOpen) - { - this.IsSnapped = false; - } - var selectedContainer = this.ItemContainerGenerator.ContainerFromItem(this.SelectedItem) as GalleryItem; - if (selectedContainer != null) selectedContainer.BringIntoView(); - }))); - } - - /// - /// Gets or sets whether control can be added to quick access toolbar - /// - public bool CanAddToQuickAccessToolBar - { - get { return (bool)this.GetValue(CanAddToQuickAccessToolBarProperty); } - set { this.SetValue(CanAddToQuickAccessToolBarProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for CanAddToQuickAccessToolBar. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty CanAddToQuickAccessToolBarProperty = RibbonControl.CanAddToQuickAccessToolBarProperty.AddOwner(typeof(InRibbonGallery), new UIPropertyMetadata(true, RibbonControl.OnCanAddToQuickAccessToolbarChanged)); - - #endregion - - #region Implementation of IScalableRibbonControl - - /// - /// Enlarge control size - /// - public void Enlarge() - { - /*currentItemsInRow++; - - if ((CanCollapseToButton) && (CurrentItemsInRow >= MinItemsInRow) && (Size == RibbonControlSize.Large)) IsCollapsed = false; - - InvalidateMeasure();*/ - if (this.IsCollapsed && (RibbonProperties.GetSize(this) == RibbonControlSize.Large)) - this.IsCollapsed = false; - else if (this.galleryPanel.MinItemsInRow < this.MaxItemsInRow) - { - this.galleryPanel.MinItemsInRow++; - this.galleryPanel.MaxItemsInRow = this.galleryPanel.MinItemsInRow; - } - else return; - this.InvalidateMeasure(); - //UpdateLayout(); - if (this.Scaled != null) - this.Scaled(this, EventArgs.Empty); - } - - /// - /// Reduce control size - /// - public void Reduce() - { - if (this.galleryPanel.MinItemsInRow > this.MinItemsInRow) - { - this.galleryPanel.MinItemsInRow--; - this.galleryPanel.MaxItemsInRow = this.galleryPanel.MinItemsInRow; - } - else if (this.CanCollapseToButton && !this.IsCollapsed) - this.IsCollapsed = true; - else return; - this.InvalidateMeasure(); - if (this.Scaled != null) - this.Scaled(this, EventArgs.Empty); - /*currentItemsInRow--; - if ((CanCollapseToButton) && (CurrentItemsInRow < MinItemsInRow)) IsCollapsed = true; - - InvalidateMeasure();*/ - } - - #endregion - - /// - /// Gets an enumerator for the logical child objects of the object. - /// - /// - /// An enumerator for the logical child objects of the object. The default is null. - /// - protected override IEnumerator LogicalChildren - { - get - { - if (this.galleryPanel != null) - { - yield return this.galleryPanel; - } - } - } - } +namespace Fluent +{ + using System; + using System.Collections; + using System.Collections.ObjectModel; + using System.Collections.Specialized; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Linq; + using System.Threading; + using System.Windows; + using System.Windows.Controls; + using System.Windows.Controls.Primitives; + using System.Windows.Data; + using System.Windows.Input; + using System.Windows.Markup; + using System.Windows.Media; + using System.Windows.Media.Imaging; + using System.Windows.Threading; + using Fluent.Extensibility; + using Fluent.Internal; + + /// + /// Represents the In-Ribbon Gallery, a gallery-based control that exposes + /// a default subset of items directly in the Ribbon. Any remaining items + /// are displayed when a drop-down menu button is clicked + /// + [ContentProperty("Items")] + [SuppressMessage("Microsoft.Maintainability", "CA1506")] + public class InRibbonGallery : Selector, IScalableRibbonControl, IDropDownControl, IRibbonControl, IQuickAccessItemProvider, IRibbonSizeChangedSink + { + #region Fields + + private ObservableCollection filters; + + private ToggleButton expandButton; + private ToggleButton dropDownButton; + + private Panel menuPanel; + + // Freezed image (created during snapping) + private Image snappedImage; + + // Is visual currently snapped + private bool isSnapped; + + private Popup popup; + + // Thumb to resize in both directions + private Thumb resizeBothThumb; + + // Thumb to resize vertical + private Thumb resizeVerticalThumb; + + private DropDownButton groupsMenuButton; + + private GalleryPanel galleryPanel; + + private ContentControl controlPresenter; + private ContentControl popupControlPresenter; + + private ScrollViewer scrollViewer; + + // Needed to prevent drop down reopen + private bool canOpenDropDown = true; + + private IInputElement focusedElement; + + private bool isButtonClicked; + + private FrameworkElement layoutRoot; + + #endregion + + #region Properties + + #region Size + + /// + /// Gets or sets Size for the element. + /// + public RibbonControlSize Size + { + get { return (RibbonControlSize)this.GetValue(SizeProperty); } + set { this.SetValue(SizeProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Size. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty SizeProperty = RibbonProperties.SizeProperty.AddOwner(typeof(InRibbonGallery)); + + #endregion + + #region SizeDefinition + + /// + /// Gets or sets SizeDefinition for element. + /// + public RibbonControlSizeDefinition SizeDefinition + { + get { return (RibbonControlSizeDefinition)this.GetValue(SizeDefinitionProperty); } + set { this.SetValue(SizeDefinitionProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for SizeDefinition. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty SizeDefinitionProperty = RibbonProperties.SizeDefinitionProperty.AddOwner(typeof(InRibbonGallery)); + + #endregion + + #region KeyTip + + /// + /// Gets or sets KeyTip for element. + /// + public string KeyTip + { + get { return (string)this.GetValue(KeyTipProperty); } + set { this.SetValue(KeyTipProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Keys. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty KeyTipProperty = Fluent.KeyTip.KeysProperty.AddOwner(typeof(InRibbonGallery)); + + #endregion + + #region Header + + /// + /// Gets or sets element Text + /// + public object Header + { + get { return this.GetValue(HeaderProperty); } + set { this.SetValue(HeaderProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Header. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty HeaderProperty = RibbonControl.HeaderProperty.AddOwner(typeof(InRibbonGallery)); + + #endregion + + #region Icon + + /// + /// Gets or sets Icon for the element + /// + public object Icon + { + get { return this.GetValue(IconProperty); } + set { this.SetValue(IconProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Icon. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IconProperty = RibbonControl.IconProperty.AddOwner(typeof(InRibbonGallery)); + + #endregion + + #region MinItemsInDropDownRow + + /// + /// Min width of the Gallery + /// + public int MinItemsInDropDownRow + { + get { return (int)this.GetValue(MinItemsInDropDownRowProperty); } + set { this.SetValue(MinItemsInDropDownRowProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for MinItemsInDropDownRow. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty MinItemsInDropDownRowProperty = + DependencyProperty.Register("MinItemsInDropDownRow", typeof(int), typeof(InRibbonGallery), new UIPropertyMetadata(1)); + + #endregion + + #region MaxItemsInDropDownRow + + /// + /// Max width of the Gallery + /// + public int MaxItemsInDropDownRow + { + get { return (int)this.GetValue(MaxItemsInDropDownRowProperty); } + set { this.SetValue(MaxItemsInDropDownRowProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for MaxItemsInDropDownRow. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty MaxItemsInDropDownRowProperty = + DependencyProperty.Register("MaxItemsInDropDownRow", typeof(int), typeof(InRibbonGallery), new UIPropertyMetadata(int.MaxValue)); + + #endregion + + #region ItemWidth + + /// + /// Gets or sets item width + /// + public double ItemWidth + { + get { return (double)this.GetValue(ItemWidthProperty); } + set { this.SetValue(ItemWidthProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for ItemWidth. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty ItemWidthProperty = + DependencyProperty.Register("ItemWidth", typeof(double), typeof(InRibbonGallery), new UIPropertyMetadata(double.NaN)); + + /// + /// Gets or sets item height + /// + public double ItemHeight + { + get { return (double)this.GetValue(ItemHeightProperty); } + set { this.SetValue(ItemHeightProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for ItemHeight. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty ItemHeightProperty = + DependencyProperty.Register("ItemHeight", typeof(double), typeof(InRibbonGallery), new UIPropertyMetadata(double.NaN)); + + #endregion + + #region GroupBy + + /// + /// Gets or sets name of property which + /// will use to group items in the Gallery. + /// + public string GroupBy + { + get { return (string)this.GetValue(GroupByProperty); } + set { this.SetValue(GroupByProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for GroupBy. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty GroupByProperty = + DependencyProperty.Register("GroupBy", typeof(string), + typeof(InRibbonGallery), new UIPropertyMetadata(null)); + + #endregion + + #region Orientation + + /// + /// Gets or sets orientation of gallery + /// + public Orientation Orientation + { + get { return (Orientation)this.GetValue(OrientationProperty); } + set { this.SetValue(OrientationProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Orientation. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty OrientationProperty = + DependencyProperty.Register("Orientation", typeof(Orientation), typeof(InRibbonGallery), new UIPropertyMetadata(Orientation.Horizontal)); + + #endregion + + #region Filters + + /// + /// Gets collection of filters + /// + public ObservableCollection Filters + { + get + { + if (this.filters == null) + { + this.filters = new ObservableCollection(); + this.filters.CollectionChanged += this.OnFilterCollectionChanged; + } + return this.filters; + } + } + + // Handle toolbar items changes + private void OnFilterCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + this.HasFilter = this.Filters.Count > 0; + this.InvalidateProperty(SelectedFilterProperty); + + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + foreach (var item in e.NewItems.OfType()) + { + if (this.groupsMenuButton != null) + { + GalleryGroupFilter filter = item; + MenuItem menuItem = new MenuItem(); + menuItem.Header = filter.Title; + menuItem.Tag = filter; + menuItem.IsDefinitive = false; + if (filter == this.SelectedFilter) menuItem.IsChecked = true; + menuItem.Click += this.OnFilterMenuItemClick; + this.groupsMenuButton.Items.Add(menuItem); + } + } + break; + case NotifyCollectionChangedAction.Remove: + foreach (var item in e.OldItems.OfType()) + { + if (this.groupsMenuButton != null) + { + this.groupsMenuButton.Items.Remove(this.GetFilterMenuItem(item)); + } + } + break; + + case NotifyCollectionChangedAction.Replace: + foreach (var item in e.OldItems.OfType()) + { + if (this.groupsMenuButton != null) + { + this.groupsMenuButton.Items.Remove(this.GetFilterMenuItem(item)); + } + } + foreach (var item in e.NewItems.OfType()) + { + if (this.groupsMenuButton != null) + { + GalleryGroupFilter filter = item; + MenuItem menuItem = new MenuItem(); + menuItem.Header = filter.Title; + menuItem.Tag = filter; + menuItem.IsDefinitive = false; + if (filter == this.SelectedFilter) menuItem.IsChecked = true; + menuItem.Click += this.OnFilterMenuItemClick; + this.groupsMenuButton.Items.Add(menuItem); + } + } + break; + case NotifyCollectionChangedAction.Reset: + + if (this.groupsMenuButton != null) + { + this.groupsMenuButton.Items.Clear(); + } + + break; + } + } + + /// + /// Gets or sets selected filter + /// + public GalleryGroupFilter SelectedFilter + { + get { return (GalleryGroupFilter)this.GetValue(SelectedFilterProperty); } + set { this.SetValue(SelectedFilterProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for SelectedFilter. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty SelectedFilterProperty = + DependencyProperty.Register("SelectedFilter", typeof(GalleryGroupFilter), + typeof(InRibbonGallery), new UIPropertyMetadata(null, OnFilterChanged, CoerceSelectedFilter)); + + // Coerce selected filter + static object CoerceSelectedFilter(DependencyObject d, object basevalue) + { + InRibbonGallery gallery = (InRibbonGallery)d; + if ((basevalue == null) && (gallery.Filters.Count > 0)) return gallery.Filters[0]; + return basevalue; + } + + // Handles filter property changed + static void OnFilterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + InRibbonGallery gallery = (InRibbonGallery)d; + GalleryGroupFilter oldFilter = e.OldValue as GalleryGroupFilter; + if (oldFilter != null) + { + System.Windows.Controls.MenuItem menuItem = gallery.GetFilterMenuItem(oldFilter); + if (menuItem != null) menuItem.IsChecked = false; + } + GalleryGroupFilter filter = e.NewValue as GalleryGroupFilter; + if (filter != null) + { + gallery.SelectedFilterTitle = filter.Title; + gallery.SelectedFilterGroups = filter.Groups; + System.Windows.Controls.MenuItem menuItem = gallery.GetFilterMenuItem(filter); + if (menuItem != null) menuItem.IsChecked = true; + } + else + { + gallery.SelectedFilterTitle = ""; + gallery.SelectedFilterGroups = null; + } + gallery.UpdateLayout(); + } + + /// + /// Gets selected filter title + /// + public string SelectedFilterTitle + { + get { return (string)this.GetValue(SelectedFilterTitleProperty); } + private set { this.SetValue(SelectedFilterTitlePropertyKey, value); } + } + + private static readonly DependencyPropertyKey SelectedFilterTitlePropertyKey = + DependencyProperty.RegisterReadOnly("SelectedFilterTitle", typeof(string), + typeof(InRibbonGallery), new UIPropertyMetadata(null)); + + /// + /// Using a DependencyProperty as the backing store for SelectedFilterTitle. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty SelectedFilterTitleProperty = SelectedFilterTitlePropertyKey.DependencyProperty; + + /// + /// Gets selected filter groups + /// + public string SelectedFilterGroups + { + get { return (string)this.GetValue(SelectedFilterGroupsProperty); } + private set { this.SetValue(SelectedFilterGroupsPropertyKey, value); } + } + + private static readonly DependencyPropertyKey SelectedFilterGroupsPropertyKey = + DependencyProperty.RegisterReadOnly("SelectedFilterGroups", typeof(string), + typeof(InRibbonGallery), new UIPropertyMetadata(null)); + + /// + /// Using a DependencyProperty as the backing store for SelectedFilterGroups. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty SelectedFilterGroupsProperty = SelectedFilterGroupsPropertyKey.DependencyProperty; + + /// + /// Gets whether gallery has selected filter + /// + public bool HasFilter + { + get { return (bool)this.GetValue(HasFilterProperty); } + private set { this.SetValue(HasFilterPropertyKey, value); } + } + + private static readonly DependencyPropertyKey HasFilterPropertyKey = DependencyProperty.RegisterReadOnly("HasFilter", typeof(bool), typeof(InRibbonGallery), new UIPropertyMetadata(false)); + + /// + /// Using a DependencyProperty as the backing store for HasFilter. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty HasFilterProperty = HasFilterPropertyKey.DependencyProperty; + + void OnFilterMenuItemClick(object sender, RoutedEventArgs e) + { + MenuItem senderItem = (MenuItem)sender; + MenuItem item = this.GetFilterMenuItem(this.SelectedFilter); + item.IsChecked = false; + senderItem.IsChecked = true; + this.SelectedFilter = senderItem.Tag as GalleryGroupFilter; + this.groupsMenuButton.IsDropDownOpen = false; + e.Handled = true; + } + + MenuItem GetFilterMenuItem(GalleryGroupFilter filter) + { + if (filter == null) return null; + if (this.groupsMenuButton == null) return null; + return this.groupsMenuButton.Items.Cast().FirstOrDefault(item => (item != null) && (item.Header.ToString() == filter.Title)); + } + + #endregion + + #region Selectable + + /// + /// Gets or sets whether gallery items can be selected + /// + public bool Selectable + { + get { return (bool)this.GetValue(SelectableProperty); } + set { this.SetValue(SelectableProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Selectable. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty SelectableProperty = + DependencyProperty.Register("Selectable", typeof(bool), + typeof(InRibbonGallery), new UIPropertyMetadata(true, OnSelectableChanged)); + + private static void OnSelectableChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + d.CoerceValue(SelectedItemProperty); + } + + #endregion + + #region IsDropDownOpen + + /// + /// Gets drop down popup + /// + public Popup DropDownPopup + { + get { return this.popup; } + } + + /// + /// Gets a value indicating whether context menu is opened + /// + public bool IsContextMenuOpened { get; set; } + + /// + /// Gets or sets whether popup is opened + /// + public bool IsDropDownOpen + { + get { return (bool)this.GetValue(IsDropDownOpenProperty); } + set { this.SetValue(IsDropDownOpenProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for IsOpen. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IsDropDownOpenProperty = + DependencyProperty.Register("IsDropDownOpen", typeof(bool), + typeof(InRibbonGallery), new UIPropertyMetadata(false)); + + #endregion + + #region ResizeMode + + /// + /// Gets or sets context menu resize mode + /// + public ContextMenuResizeMode ResizeMode + { + get { return (ContextMenuResizeMode)this.GetValue(ResizeModeProperty); } + set { this.SetValue(ResizeModeProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for ResizeMode. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty ResizeModeProperty = + DependencyProperty.Register("ResizeMode", typeof(ContextMenuResizeMode), typeof(InRibbonGallery), new UIPropertyMetadata(ContextMenuResizeMode.None)); + + #endregion + + #region CanCollapseToButton + + /// + /// Gets or sets whether InRibbonGallery + /// + public bool CanCollapseToButton + { + get { return (bool)this.GetValue(CanCollapseToButtonProperty); } + set { this.SetValue(CanCollapseToButtonProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for CanCollapseToButton. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty CanCollapseToButtonProperty = + DependencyProperty.Register("CanCollapseToButton", typeof(bool), typeof(InRibbonGallery), new UIPropertyMetadata(true)); + + #endregion + + #region IsCollapsed + + /// + /// Gets whether InRibbonGallery is collapsed to button + /// + public bool IsCollapsed + { + get { return (bool)this.GetValue(IsCollapsedProperty); } + set { this.SetValue(IsCollapsedProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for IsCollapsed. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IsCollapsedProperty = + DependencyProperty.Register("IsCollapsed", typeof(bool), typeof(InRibbonGallery), new UIPropertyMetadata(false)); + + #endregion + + #region LargeIcon + + /// + /// Button large icon + /// + public ImageSource LargeIcon + { + get { return (ImageSource)this.GetValue(LargeIconProperty); } + set { this.SetValue(LargeIconProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for SmallIcon. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty LargeIconProperty = + DependencyProperty.Register("LargeIcon", typeof(ImageSource), typeof(InRibbonGallery), new UIPropertyMetadata(null)); + + #endregion + + #region Snapping + + /// + /// Snaps / Unsnaps the Visual + /// (remove visuals and substitute with freezed image) + /// + public bool IsSnapped + { + get + { + return this.isSnapped; + } + set + { + if (value == this.isSnapped) return; + if (this.IsCollapsed) return; + + if (!this.IsVisible) return; + + if ((value) && (((int)this.ActualWidth > 0) && ((int)this.ActualHeight > 0))) + { + + // Render the freezed image + RenderOptions.SetBitmapScalingMode(this.snappedImage, BitmapScalingMode.NearestNeighbor); + RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap((int)this.galleryPanel.ActualWidth, + (int)this.galleryPanel.ActualHeight, 96, 96, + PixelFormats.Pbgra32); + renderTargetBitmap.Render(this.galleryPanel); + this.snappedImage.Source = renderTargetBitmap; + this.snappedImage.FlowDirection = this.FlowDirection; + this.snappedImage.Width = this.galleryPanel.ActualWidth; + this.snappedImage.Height = this.galleryPanel.ActualHeight; + this.snappedImage.Visibility = Visibility.Visible; + this.isSnapped = value; + } + else + { + this.snappedImage.Visibility = Visibility.Collapsed; + this.isSnapped = value; + this.InvalidateVisual(); + } + + this.InvalidateVisual(); + } + } + + #endregion + + #region Menu + + /// + /// Gets or sets menu to show in combo box bottom + /// + public RibbonMenu Menu + { + get { return (RibbonMenu)this.GetValue(MenuProperty); } + set { this.SetValue(MenuProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Menu. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty MenuProperty = + DependencyProperty.Register("Menu", typeof(RibbonMenu), typeof(InRibbonGallery), new UIPropertyMetadata(null)); + + #endregion + + #region Min/Max Sizes + + /// + /// Gets or sets max count of items in row + /// + public int MaxItemsInRow + { + get { return (int)this.GetValue(MaxItemsInRowProperty); } + set { this.SetValue(MaxItemsInRowProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for MaxItemsInRow. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty MaxItemsInRowProperty = + DependencyProperty.Register("MaxItemsInRow", typeof(int), typeof(InRibbonGallery), new UIPropertyMetadata(8, OnMaxItemsInRowChanged)); + + /// + /// Gets or sets min count of items in row + /// + public int MinItemsInRow + { + get { return (int)this.GetValue(MinItemsInRowProperty); } + set { this.SetValue(MinItemsInRowProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for MaxItemsInRow. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty MinItemsInRowProperty = + DependencyProperty.Register("MinItemsInRow", typeof(int), typeof(InRibbonGallery), new UIPropertyMetadata(1)); + + private static void OnMaxItemsInRowChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + InRibbonGallery gal = d as InRibbonGallery; + int minItemsInRow = (int)e.NewValue; + if (!gal.IsDropDownOpen && (gal.galleryPanel != null) && (gal.galleryPanel.MinItemsInRow < minItemsInRow)) + { + gal.galleryPanel.MinItemsInRow = minItemsInRow; + gal.galleryPanel.MaxItemsInRow = minItemsInRow; + } + } + + #endregion + + #region MaxDropDownHeight + + /// + /// Get or sets max height of drop down popup + /// + public double MaxDropDownHeight + { + get { return (double)this.GetValue(MaxDropDownHeightProperty); } + set { this.SetValue(MaxDropDownHeightProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for MaxDropDownHeight. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty MaxDropDownHeightProperty = + DependencyProperty.Register("MaxDropDownHeight", typeof(double), typeof(InRibbonGallery), new UIPropertyMetadata(SystemParameters.PrimaryScreenHeight / 3.0)); + + #endregion + + #region MaxDropDownWidth + + /// + /// Get or sets max width of drop down popup + /// + public double MaxDropDownWidth + { + get { return (double)this.GetValue(MaxDropDownWidthProperty); } + set { this.SetValue(MaxDropDownWidthProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for MaxDropDownWidth. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty MaxDropDownWidthProperty = + DependencyProperty.Register("MaxDropDownWidth", typeof(double), typeof(InRibbonGallery), new UIPropertyMetadata(SystemParameters.PrimaryScreenWidth / 3.0)); + + #endregion + + #region DropDownHeight + + /// + /// Gets or sets initial dropdown height + /// + public double DropDownHeight + { + get { return (double)this.GetValue(DropDownHeightProperty); } + set { this.SetValue(DropDownHeightProperty, value); } + } + + /// + /// /Using a DependencyProperty as the backing store for DropDownHeight. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty DropDownHeightProperty = + DependencyProperty.Register("DropDownHeight", typeof(double), typeof(InRibbonGallery), new UIPropertyMetadata(double.NaN)); + + #endregion + + #region DropDownWidth + + /// + /// Gets or sets initial dropdown width + /// + public double DropDownWidth + { + get { return (double)this.GetValue(DropDownWidthProperty); } + set { this.SetValue(DropDownWidthProperty, value); } + } + + /// + /// /Using a DependencyProperty as the backing store for DropDownWidth. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty DropDownWidthProperty = + DependencyProperty.Register("DropDownWidth", typeof(double), typeof(InRibbonGallery), new UIPropertyMetadata(double.NaN)); + + #endregion + + #region ShowPopupOnTop + + /// + /// Gets a value indicating whether popup is shown on top; + /// + public bool ShowPopupOnTop + { + get { return (bool)this.GetValue(ShowPopupOnTopProperty); } + private set { this.SetValue(ShowPopupOnTopPropertyKey, value); } + } + + // + private static readonly DependencyPropertyKey ShowPopupOnTopPropertyKey = DependencyProperty.RegisterReadOnly("ShowPopupOnTop", typeof(bool), typeof(InRibbonGallery), new UIPropertyMetadata(false)); + + /// + /// Using a DependencyProperty as the backing store for ShowPopupOnTop. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty ShowPopupOnTopProperty = ShowPopupOnTopPropertyKey.DependencyProperty; + + #endregion + + #endregion + + #region Events + + /// + /// Occurs when control is scaled + /// + public event EventHandler Scaled; + + /// + /// Occurs when context menu is opened + /// + public event EventHandler DropDownOpened; + + /// + /// Occurs when context menu is closed + /// + public event EventHandler DropDownClosed; + + #endregion + + #region Constructors + + /// + /// Static constructor + /// + [SuppressMessage("Microsoft.Performance", "CA1810")] + static InRibbonGallery() + { + Type type = typeof(InRibbonGallery); + ToolTipService.Attach(type); + PopupService.Attach(type); + ContextMenuService.Attach(type); + DefaultStyleKeyProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(type)); + StyleProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceStyle))); + SelectedItemProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(null, CoerceSelectedItem)); + } + + // Coerce object style + static object OnCoerceStyle(DependencyObject d, object basevalue) + { + if (basevalue == null) + { + basevalue = (d as FrameworkElement).TryFindResource(typeof(InRibbonGallery)); + } + + return basevalue; + } + + // Coerce selected item + private static object CoerceSelectedItem(DependencyObject d, object basevalue) + { + InRibbonGallery gallery = (InRibbonGallery)d; + if (!gallery.Selectable) + { + GalleryItem galleryItem = (GalleryItem)gallery.ItemContainerGenerator.ContainerFromItem(basevalue); + if (basevalue != null && galleryItem != null) galleryItem.IsSelected = false; + return null; + } + return basevalue; + } + + /// + /// Default constructor + /// + public InRibbonGallery() + { + ContextMenuService.Coerce(this); + } + + #endregion + + #region Overrides + + /// + /// Handles key tip pressed + /// + public void OnKeyTipPressed() + { + this.IsDropDownOpen = true; + } + + /// + /// Handles back navigation with KeyTips + /// + public void OnKeyTipBack() + { + this.IsDropDownOpen = false; + } + + /// + /// Called when the selection changes. + /// + /// The event data. + protected override void OnSelectionChanged(SelectionChangedEventArgs e) + { + foreach (var item in e.RemovedItems) + { + GalleryItem itemContainer = (this.ItemContainerGenerator.ContainerFromItem(item) as GalleryItem); + if (itemContainer != null) itemContainer.IsSelected = false; + } + + foreach (var item in e.AddedItems) + { + GalleryItem itemContainer = (this.ItemContainerGenerator.ContainerFromItem(item) as GalleryItem); + if (itemContainer != null) itemContainer.IsSelected = true; + } + //if (IsDropDownOpen) IsDropDownOpen = false; + base.OnSelectionChanged(e); + } + + /// + /// When overridden in a derived class, is invoked whenever application + /// code or internal processes call ApplyTemplate + /// + public override void OnApplyTemplate() + { + this.layoutRoot = this.GetTemplateChild("PART_LayoutRoot") as FrameworkElement; + + if (this.expandButton != null) + this.expandButton.Click -= this.OnExpandClick; + this.expandButton = this.GetTemplateChild("PART_ExpandButton") as ToggleButton; + if (this.expandButton != null) + this.expandButton.Click += this.OnExpandClick; + + if (this.dropDownButton != null) + this.dropDownButton.Click -= this.OnDropDownClick; + this.dropDownButton = this.GetTemplateChild("PART_DropDownButton") as ToggleButton; + if (this.dropDownButton != null) + this.dropDownButton.Click += this.OnDropDownClick; + + if (this.popup != null) + { + this.popup.Opened -= this.OnDropDownOpened; + this.popup.Closed -= this.OnDropDownClosed; + + this.popup.PreviewMouseLeftButtonUp -= this.OnPopupPreviewMouseUp; + this.popup.PreviewMouseLeftButtonDown -= this.OnPopupPreviewMouseDown; + } + + this.popup = this.GetTemplateChild("PART_Popup") as Popup; + + if (this.popup != null) + { + this.popup.Opened += this.OnDropDownOpened; + this.popup.Closed += this.OnDropDownClosed; + + this.popup.PreviewMouseLeftButtonUp += this.OnPopupPreviewMouseUp; + this.popup.PreviewMouseLeftButtonDown += this.OnPopupPreviewMouseDown; + + KeyboardNavigation.SetControlTabNavigation(this.popup, KeyboardNavigationMode.Cycle); + KeyboardNavigation.SetDirectionalNavigation(this.popup, KeyboardNavigationMode.Cycle); + KeyboardNavigation.SetTabNavigation(this.popup, KeyboardNavigationMode.Cycle); + } + + if (this.resizeVerticalThumb != null) + { + this.resizeVerticalThumb.DragDelta -= this.OnResizeVerticalDelta; + } + this.resizeVerticalThumb = this.GetTemplateChild("PART_ResizeVerticalThumb") as Thumb; + if (this.resizeVerticalThumb != null) + { + this.resizeVerticalThumb.DragDelta += this.OnResizeVerticalDelta; + } + + if (this.resizeBothThumb != null) + { + this.resizeBothThumb.DragDelta -= this.OnResizeBothDelta; + } + this.resizeBothThumb = this.GetTemplateChild("PART_ResizeBothThumb") as Thumb; + if (this.resizeBothThumb != null) + { + this.resizeBothThumb.DragDelta += this.OnResizeBothDelta; + } + + this.menuPanel = this.GetTemplateChild("PART_MenuPanel") as Panel; + + if (this.groupsMenuButton != null) + this.groupsMenuButton.Items.Clear(); + this.groupsMenuButton = this.GetTemplateChild("PART_FilterDropDownButton") as DropDownButton; + if (this.groupsMenuButton != null) + { + for (int i = 0; i < this.Filters.Count; i++) + { + MenuItem item = new MenuItem(); + item.Header = this.Filters[i].Title; + item.Tag = this.Filters[i]; + item.IsDefinitive = false; + if (this.Filters[i] == this.SelectedFilter) item.IsChecked = true; + item.Click += this.OnFilterMenuItemClick; + this.groupsMenuButton.Items.Add(item); + } + } + + this.galleryPanel = this.GetTemplateChild("PART_GalleryPanel") as GalleryPanel; + + if (this.galleryPanel != null) + { + this.galleryPanel.MinItemsInRow = this.MaxItemsInRow; + this.galleryPanel.MaxItemsInRow = this.MaxItemsInRow; + } + + this.snappedImage = this.GetTemplateChild("PART_FakeImage") as Image; + + this.controlPresenter = this.GetTemplateChild("PART_ContentPresenter") as ContentControl; + this.popupControlPresenter = this.GetTemplateChild("PART_PopupContentPresenter") as ContentControl; + + this.scrollViewer = this.GetTemplateChild("PART_ScrollViewer") as ScrollViewer; + } + + private void OnPopupPreviewMouseUp(object sender, MouseButtonEventArgs e) + { + // Ignore mouse up when mouse donw is on expand button + if (this.isButtonClicked) + { + this.isButtonClicked = false; + e.Handled = true; + } + } + + private void OnPopupPreviewMouseDown(object sender, MouseButtonEventArgs e) + { + this.isButtonClicked = false; + } + + private void OnExpandClick(object sender, RoutedEventArgs e) + { + this.isButtonClicked = true; + } + + private void OnDropDownClick(object sender, RoutedEventArgs e) + { + if (this.canOpenDropDown) + this.IsDropDownOpen = true; + } + + // Handles drop down opened + void OnDropDownClosed(object sender, EventArgs e) + { + this.galleryPanel.Width = double.NaN; + this.galleryPanel.IsGrouped = false; + this.galleryPanel.MinItemsInRow = this.MinItemsInRow; + this.galleryPanel.MaxItemsInRow = this.MaxItemsInRow; + this.galleryPanel.UpdateMinAndMaxWidth(); + + this.popupControlPresenter.Content = null; + this.controlPresenter.Content = this.galleryPanel; + this.Dispatcher.BeginInvoke(DispatcherPriority.SystemIdle, (ThreadStart)(() => + { + if ((this.quickAccessGallery == null) || ((this.quickAccessGallery != null) && (!this.quickAccessGallery.IsDropDownOpen))) + { + this.IsSnapped = false; + } + })); + + //snappedImage.Visibility = Visibility.Collapsed; + if (this.DropDownClosed != null) + this.DropDownClosed(this, e); + if (Mouse.Captured == this) Mouse.Capture(null); + this.Dispatcher.BeginInvoke(DispatcherPriority.SystemIdle, (ThreadStart)(() => + { + GalleryItem selectedContainer = this.ItemContainerGenerator.ContainerFromItem(this.SelectedItem) as GalleryItem; + if (selectedContainer != null) selectedContainer.BringIntoView(); + + })); + this.dropDownButton.IsChecked = false; + this.canOpenDropDown = true; + } + + // Handles drop down closed + private void OnDropDownOpened(object sender, EventArgs e) + { + this.IsSnapped = true; + + this.controlPresenter.Content = null; + this.popupControlPresenter.Content = this.galleryPanel; + this.galleryPanel.Width = double.NaN; + this.scrollViewer.Height = double.NaN; + + if (this.DropDownOpened != null) + { + this.DropDownOpened(this, e); + } + + this.galleryPanel.MinItemsInRow = this.MinItemsInDropDownRow; + this.galleryPanel.MaxItemsInRow = this.MaxItemsInDropDownRow; + this.galleryPanel.UpdateMinAndMaxWidth(); + + this.galleryPanel.IsGrouped = true; + this.dropDownButton.IsChecked = true; + this.canOpenDropDown = false; + + Mouse.Capture(this, CaptureMode.SubTree); + + this.focusedElement = Keyboard.FocusedElement; + Debug.WriteLine("Focused element - " + this.focusedElement); + + if (this.focusedElement != null) + { + this.focusedElement.LostKeyboardFocus += this.OnFocusedElementLostKeyboardFocus; + this.focusedElement.PreviewKeyDown += this.OnFocusedElementPreviewKeyDown; + } + + //if (ResizeMode != ContextMenuResizeMode.None) + { + this.scrollViewer.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); + + var initialHeight = Math.Min(RibbonControl.GetControlWorkArea(this).Height, this.MaxDropDownHeight); + + if (!double.IsNaN(this.DropDownHeight)) + { + initialHeight = Math.Min(this.DropDownHeight, this.MaxDropDownHeight); + } + + var initialWidth = Math.Min(RibbonControl.GetControlWorkArea(this).Height, this.MaxDropDownWidth); + + if (!double.IsNaN(this.DropDownWidth)) + { + initialWidth = Math.Min(this.DropDownWidth, this.MaxDropDownWidth); + } + + double menuHeight = 0; + double menuWidth = 0; + + if (this.Menu != null) + { + this.Menu.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); + menuHeight = this.Menu.DesiredSize.Height; + menuWidth = this.Menu.DesiredSize.Width; + } + + if (this.scrollViewer.DesiredSize.Height > initialHeight) + { + this.scrollViewer.Height = initialHeight - menuHeight; + + if (this.scrollViewer.Height < this.galleryPanel.GetItemSize().Height) + { + this.scrollViewer.Height = this.galleryPanel.GetItemSize().Height; + } + } + + if (this.scrollViewer.DesiredSize.Width > initialWidth) + { + this.scrollViewer.Width = initialWidth - menuWidth; + + if (this.scrollViewer.Width < this.galleryPanel.GetItemSize().Width) + { + this.scrollViewer.Width = this.galleryPanel.GetItemSize().Width; + } + } + } + } + + /// + /// Handles size property changing + /// + /// Previous value + /// Current value + public void OnSizePropertyChanged(RibbonControlSize previous, RibbonControlSize current) + { + if (this.CanCollapseToButton) + { + if (current == RibbonControlSize.Large + && this.galleryPanel.MinItemsInRow > this.MinItemsInRow) + { + this.IsCollapsed = false; + } + else + { + this.IsCollapsed = true; + } + } + else + { + this.IsCollapsed = false; + } + } + + /// + /// Creates or identifies the element that is used to display the given item. + /// + /// The element that is used to display the given item. + protected override DependencyObject GetContainerForItemOverride() + { + return new GalleryItem(); + } + + /// + /// Determines if the specified item is (or is eligible to be) its own container. + /// + /// The item to check. + /// + protected override bool IsItemItsOwnContainerOverride(object item) + { + return item is GalleryItem; + } + + /// + /// Invoked when the event is received. + /// + /// Information about the event. + protected override void OnKeyDown(KeyEventArgs e) + { + if (e.Key == Key.Escape) + { + this.IsDropDownOpen = false; + } + + base.OnKeyDown(e); + } + + private void OnFocusedElementPreviewKeyDown(object sender, KeyEventArgs e) + { + if (e.Key == Key.Escape) + { + this.IsDropDownOpen = false; + } + } + + private void OnFocusedElementLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e) + { + this.focusedElement.LostKeyboardFocus -= this.OnFocusedElementLostKeyboardFocus; + this.focusedElement.PreviewKeyDown -= this.OnFocusedElementPreviewKeyDown; + } + + #endregion + + #region Private Methods + + // Handles resize both drag + private void OnResizeBothDelta(object sender, DragDeltaEventArgs e) + { + this.OnResizeVerticalDelta(sender, e); + + this.menuPanel.Width = double.NaN; + + if (double.IsNaN(this.galleryPanel.Width)) + { + this.galleryPanel.Width = this.galleryPanel.ActualWidth; + } + + this.galleryPanel.Width = Math.Max(this.layoutRoot.ActualWidth, this.galleryPanel.Width + e.HorizontalChange); + + } + + // Handles resize vertical drag + private void OnResizeVerticalDelta(object sender, DragDeltaEventArgs e) + { + if (double.IsNaN(this.scrollViewer.Height)) + { + this.scrollViewer.Height = this.scrollViewer.ActualHeight; + } + + this.scrollViewer.Height = Math.Max(this.layoutRoot.ActualHeight, Math.Min(Math.Max(this.galleryPanel.GetItemSize().Height, this.scrollViewer.Height + e.VerticalChange), this.MaxDropDownHeight)); + } + + #endregion + + #region QuickAccess + + /// + /// Gets control which represents shortcut item. + /// This item MUST be syncronized with the original + /// and send command to original one control. + /// + /// Control which represents shortcut item + public virtual FrameworkElement CreateQuickAccessItem() + { + var gallery = new InRibbonGallery(); + RibbonControl.BindQuickAccessItem(this, gallery); + RibbonControl.Bind(this, gallery, "GroupBy", GroupByProperty, BindingMode.OneWay); + RibbonControl.Bind(this, gallery, "ItemHeight", ItemHeightProperty, BindingMode.OneWay); + RibbonControl.Bind(this, gallery, "ItemWidth", ItemWidthProperty, BindingMode.OneWay); + RibbonControl.Bind(this, gallery, "ResizeMode", ResizeModeProperty, BindingMode.OneWay); + RibbonControl.Bind(this, gallery, "MinItemsInDropDownRow", MinItemsInDropDownRowProperty, BindingMode.OneWay); + RibbonControl.Bind(this, gallery, "MaxItemsInDropDownRow", MaxItemsInDropDownRowProperty, BindingMode.OneWay); + + RibbonControl.Bind(this, gallery, "DisplayMemberPath", DisplayMemberPathProperty, BindingMode.OneWay); + RibbonControl.Bind(this, gallery, "GroupStyleSelector", GroupStyleSelectorProperty, BindingMode.OneWay); + RibbonControl.Bind(this, gallery, "ItemContainerStyle", ItemContainerStyleProperty, BindingMode.OneWay); + RibbonControl.Bind(this, gallery, "ItemsPanel", ItemsPanelProperty, BindingMode.OneWay); + RibbonControl.Bind(this, gallery, "ItemStringFormat", ItemStringFormatProperty, BindingMode.OneWay); + RibbonControl.Bind(this, gallery, "ItemTemplate", ItemTemplateProperty, BindingMode.OneWay); + RibbonControl.Bind(this, gallery, "SelectedValuePath", SelectedValuePathProperty, BindingMode.OneWay); + RibbonControl.Bind(this, gallery, "MaxDropDownWidth", MaxDropDownWidthProperty, BindingMode.OneWay); + RibbonControl.Bind(this, gallery, "MaxDropDownHeight", MaxDropDownHeightProperty, BindingMode.OneWay); + + gallery.DropDownOpened += this.OnQuickAccessOpened; + if (this.DropDownClosed != null) gallery.DropDownClosed += this.DropDownClosed; + if (this.DropDownOpened != null) gallery.DropDownOpened += this.DropDownOpened; + + RibbonProperties.SetSize(gallery, RibbonControlSize.Small); + this.quickAccessGallery = gallery; + return gallery; + } + + private object selectedItem; + private InRibbonGallery quickAccessGallery; + + private void OnQuickAccessOpened(object sender, EventArgs e) + { + for (var i = 0; i < this.Filters.Count; i++) + { + this.quickAccessGallery.Filters.Add(this.Filters[i]); + } + + this.quickAccessGallery.SelectedFilter = this.SelectedFilter; + + this.quickAccessGallery.DropDownClosed += this.OnQuickAccessMenuClosedOrUnloaded; + this.quickAccessGallery.Unloaded += this.OnQuickAccessMenuClosedOrUnloaded; + + this.UpdateLayout(); + this.Dispatcher.BeginInvoke(DispatcherPriority.Render, ((Action)(this.Freeze))); + } + + private void OnQuickAccessMenuClosedOrUnloaded(object sender, EventArgs e) + { + this.quickAccessGallery.DropDownClosed -= this.OnQuickAccessMenuClosedOrUnloaded; + this.quickAccessGallery.Unloaded -= this.OnQuickAccessMenuClosedOrUnloaded; + + this.SelectedFilter = this.quickAccessGallery.SelectedFilter; + this.quickAccessGallery.Filters.Clear(); + this.Unfreeze(); + } + + private void Freeze() + { + this.IsSnapped = true; + this.selectedItem = this.SelectedItem; + this.SelectedItem = null; + + ItemsControlHelper.MoveItemsToDifferentControl(this, this.quickAccessGallery); + + this.quickAccessGallery.SelectedItem = this.selectedItem; + this.quickAccessGallery.Menu = this.Menu; + this.Menu = null; + //quickAccessGallery.IsSnapped = false; + } + + private void Unfreeze() + { + this.selectedItem = this.quickAccessGallery.SelectedItem; + //quickAccessGallery.IsSnapped = true; + this.quickAccessGallery.SelectedItem = null; + + ItemsControlHelper.MoveItemsToDifferentControl(this.quickAccessGallery, this); + + this.SelectedItem = this.selectedItem; + this.Menu = this.quickAccessGallery.Menu; + this.quickAccessGallery.Menu = null; + + if (!this.IsDropDownOpen) + { + if (this.controlPresenter != null) + { + this.controlPresenter.Content = null; + } + + if (this.popupControlPresenter != null) + { + this.popupControlPresenter.Content = this.galleryPanel; + } + + if (this.galleryPanel != null) + { + this.galleryPanel.IsGrouped = true; + this.galleryPanel.IsGrouped = false; + } + + if (this.popupControlPresenter != null) + { + this.popupControlPresenter.Content = null; + } + + if (this.controlPresenter != null) + { + this.controlPresenter.Content = this.galleryPanel; + } + } + + this.Dispatcher.BeginInvoke(DispatcherPriority.SystemIdle, ((ThreadStart)(() => + { + if (!this.IsDropDownOpen) + { + this.IsSnapped = false; + } + var selectedContainer = this.ItemContainerGenerator.ContainerFromItem(this.SelectedItem) as GalleryItem; + if (selectedContainer != null) selectedContainer.BringIntoView(); + }))); + } + + /// + /// Gets or sets whether control can be added to quick access toolbar + /// + public bool CanAddToQuickAccessToolBar + { + get { return (bool)this.GetValue(CanAddToQuickAccessToolBarProperty); } + set { this.SetValue(CanAddToQuickAccessToolBarProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for CanAddToQuickAccessToolBar. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty CanAddToQuickAccessToolBarProperty = RibbonControl.CanAddToQuickAccessToolBarProperty.AddOwner(typeof(InRibbonGallery), new UIPropertyMetadata(true, RibbonControl.OnCanAddToQuickAccessToolbarChanged)); + + #endregion + + #region Implementation of IScalableRibbonControl + + /// + /// Enlarge control size + /// + public void Enlarge() + { + /*currentItemsInRow++; + + if ((CanCollapseToButton) && (CurrentItemsInRow >= MinItemsInRow) && (Size == RibbonControlSize.Large)) IsCollapsed = false; + + InvalidateMeasure();*/ + if (this.IsCollapsed && (RibbonProperties.GetSize(this) == RibbonControlSize.Large)) + this.IsCollapsed = false; + else if (this.galleryPanel.MinItemsInRow < this.MaxItemsInRow) + { + this.galleryPanel.MinItemsInRow++; + this.galleryPanel.MaxItemsInRow = this.galleryPanel.MinItemsInRow; + } + else return; + this.InvalidateMeasure(); + //UpdateLayout(); + if (this.Scaled != null) + this.Scaled(this, EventArgs.Empty); + } + + /// + /// Reduce control size + /// + public void Reduce() + { + if (this.galleryPanel.MinItemsInRow > this.MinItemsInRow) + { + this.galleryPanel.MinItemsInRow--; + this.galleryPanel.MaxItemsInRow = this.galleryPanel.MinItemsInRow; + } + else if (this.CanCollapseToButton && !this.IsCollapsed) + this.IsCollapsed = true; + else return; + this.InvalidateMeasure(); + if (this.Scaled != null) + this.Scaled(this, EventArgs.Empty); + /*currentItemsInRow--; + if ((CanCollapseToButton) && (CurrentItemsInRow < MinItemsInRow)) IsCollapsed = true; + + InvalidateMeasure();*/ + } + + #endregion + + /// + /// Gets an enumerator for the logical child objects of the object. + /// + /// + /// An enumerator for the logical child objects of the object. The default is null. + /// + protected override IEnumerator LogicalChildren + { + get + { + if (this.galleryPanel != null) + { + yield return this.galleryPanel; + } + } + } + } } \ No newline at end of file diff --git a/Fluent/Controls/KeyTip.cs b/Fluent.Ribbon/Controls/KeyTip.cs similarity index 97% rename from Fluent/Controls/KeyTip.cs rename to Fluent.Ribbon/Controls/KeyTip.cs index 53b2a53ea..0de545b3c 100644 --- a/Fluent/Controls/KeyTip.cs +++ b/Fluent.Ribbon/Controls/KeyTip.cs @@ -1,238 +1,238 @@ -using System.Diagnostics.CodeAnalysis; -using System.Windows; -using System.Windows.Controls; - -namespace Fluent -{ - /// - /// Represents KeyTip control - /// - public class KeyTip : Label - { - #region Keys Attached Property - - /// - /// Using a DependencyProperty as the backing store for Keys. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty KeysProperty = DependencyProperty.RegisterAttached( - "Keys", - typeof(string), - typeof(KeyTip), - new FrameworkPropertyMetadata(null, KeysPropertyChanged) - ); - - static void KeysPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - - } - - /// - /// Sets value of attached property Keys for the given element - /// - /// The given element - /// Value - public static void SetKeys(DependencyObject element, string value) - { - element.SetValue(KeysProperty, value); - } - - /// - /// Gets value of the attached property Keys of the given element - /// - /// The given element - [System.ComponentModel.DisplayName("Keys"), - AttachedPropertyBrowsableForChildren(IncludeDescendants = true), - System.ComponentModel.Category("KeyTips"), - System.ComponentModel.Description("Key sequence for the given element")] - public static string GetKeys(DependencyObject element) - { - return (string)element.GetValue(KeysProperty); - } - - #endregion - - #region AutoPlacement Attached Property - - /// - /// Using a DependencyProperty as the backing store for AutoPlacement. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty AutoPlacementProperty = DependencyProperty.RegisterAttached( - "AutoPlacement", - typeof(bool), - typeof(KeyTip), - new FrameworkPropertyMetadata(true) - ); - - - /// - /// Sets whether key tip placement is auto - /// or defined by alignment and margin properties - /// - /// The given element - /// Value - public static void SetAutoPlacement(DependencyObject element, bool value) - { - element.SetValue(AutoPlacementProperty, value); - } - - /// - /// Gets whether key tip placement is auto - /// or defined by alignment and margin properties - /// - /// The given element - [System.ComponentModel.DisplayName("AutoPlacement"), - AttachedPropertyBrowsableForChildren(IncludeDescendants = true), - System.ComponentModel.Category("KeyTips"), - System.ComponentModel.Description("Whether key tip placement is auto or defined by alignment and margin properties")] - public static bool GetAutoPlacement(DependencyObject element) - { - return (bool)element.GetValue(AutoPlacementProperty); - } - - #endregion - - #region HorizontalAlignment Attached Property - - /// - /// Using a DependencyProperty as the backing store for HorizontalAlignment. - /// This enables animation, styling, binding, etc... - /// - public static new readonly DependencyProperty HorizontalAlignmentProperty = DependencyProperty.RegisterAttached( - "HorizontalAlignment", - typeof(HorizontalAlignment), - typeof(KeyTip), - new FrameworkPropertyMetadata(HorizontalAlignment.Center) - ); - - - /// - /// Sets Horizontal Alignment of the key tip - /// - /// The given element - /// Value - public static void SetHorizontalAlignment(DependencyObject element, HorizontalAlignment value) - { - element.SetValue(HorizontalAlignmentProperty, value); - } - - /// - /// Gets Horizontal alignment of the key tip - /// - /// The given element - [System.ComponentModel.DisplayName("HorizontalAlignment"), - AttachedPropertyBrowsableForChildren(IncludeDescendants = true), - System.ComponentModel.Category("KeyTips"), - System.ComponentModel.Description("Horizontal alignment of the key tip")] - public static HorizontalAlignment GetHorizontalAlignment(DependencyObject element) - { - return (HorizontalAlignment)element.GetValue(HorizontalAlignmentProperty); - } - - #endregion - - #region VerticalAlignment Attached Property - - /// - /// Gets vertical alignment of the key tip - /// - /// The given element - [System.ComponentModel.DisplayName("VerticalAlignment"), - AttachedPropertyBrowsableForChildren(IncludeDescendants = true), - System.ComponentModel.Category("KeyTips"), - System.ComponentModel.Description("Vertical alignment of the key tip")] - public static VerticalAlignment GetVerticalAlignment(DependencyObject element) - { - return (VerticalAlignment)element.GetValue(VerticalAlignmentProperty); - } - - /// - /// Sets vertical alignment of the key tip - /// - /// The given element - /// Value - public static void SetVerticalAlignment(DependencyObject obj, VerticalAlignment value) - { - obj.SetValue(VerticalAlignmentProperty, value); - } - - /// - /// Using a DependencyProperty as the backing store for VerticalAlignment. - /// This enables animation, styling, binding, etc... - /// - public static new readonly DependencyProperty VerticalAlignmentProperty = - DependencyProperty.RegisterAttached("VerticalAlignment", - typeof(VerticalAlignment), typeof(KeyTip), - new UIPropertyMetadata(VerticalAlignment.Center)); - - #endregion - - #region Margin Attached Property - - /// - /// Gets margin of the key tip - /// - /// The key tip - /// Margin - [System.ComponentModel.DisplayName("Margin"), - AttachedPropertyBrowsableForChildren(IncludeDescendants = true), - System.ComponentModel.Category("KeyTips"), - System.ComponentModel.Description("Margin of the key tip")] - public static Thickness GetMargin(DependencyObject obj) - { - return (Thickness)obj.GetValue(MarginProperty); - } - - /// - /// Sets margin of the key tip - /// - /// The key tip - /// Value - public static void SetMargin(DependencyObject obj, Thickness value) - { - obj.SetValue(MarginProperty, value); - } - - /// - /// Using a DependencyProperty as the backing store for Margin. - /// This enables animation, styling, binding, etc... - /// - public static new readonly DependencyProperty MarginProperty = - DependencyProperty.RegisterAttached("Margin", typeof(Thickness), typeof(KeyTip), new UIPropertyMetadata(new Thickness())); - - #endregion - - #region Initialization - - // Static constructor - [SuppressMessage("Microsoft.Performance", "CA1810")] - static KeyTip() - { - // Override metadata to allow slyling - //StyleProperty.OverrideMetadata(typeof(KeyTip), new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceStyle))); - DefaultStyleKeyProperty.OverrideMetadata(typeof(KeyTip), new FrameworkPropertyMetadata(typeof(KeyTip))); - StyleProperty.OverrideMetadata(typeof(KeyTip), new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceStyle))); - } - - // Coerce object style - static object OnCoerceStyle(DependencyObject d, object basevalue) - { - if (basevalue == null) - { - basevalue = (d as FrameworkElement).TryFindResource(typeof(KeyTip)); - } - - return basevalue; - } - - /// - /// Default constrctor - /// - public KeyTip() - { - } - - #endregion - } -} +using System.Diagnostics.CodeAnalysis; +using System.Windows; +using System.Windows.Controls; + +namespace Fluent +{ + /// + /// Represents KeyTip control + /// + public class KeyTip : Label + { + #region Keys Attached Property + + /// + /// Using a DependencyProperty as the backing store for Keys. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty KeysProperty = DependencyProperty.RegisterAttached( + "Keys", + typeof(string), + typeof(KeyTip), + new FrameworkPropertyMetadata(null, KeysPropertyChanged) + ); + + static void KeysPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + + } + + /// + /// Sets value of attached property Keys for the given element + /// + /// The given element + /// Value + public static void SetKeys(DependencyObject element, string value) + { + element.SetValue(KeysProperty, value); + } + + /// + /// Gets value of the attached property Keys of the given element + /// + /// The given element + [System.ComponentModel.DisplayName("Keys"), + AttachedPropertyBrowsableForChildren(IncludeDescendants = true), + System.ComponentModel.Category("KeyTips"), + System.ComponentModel.Description("Key sequence for the given element")] + public static string GetKeys(DependencyObject element) + { + return (string)element.GetValue(KeysProperty); + } + + #endregion + + #region AutoPlacement Attached Property + + /// + /// Using a DependencyProperty as the backing store for AutoPlacement. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty AutoPlacementProperty = DependencyProperty.RegisterAttached( + "AutoPlacement", + typeof(bool), + typeof(KeyTip), + new FrameworkPropertyMetadata(true) + ); + + + /// + /// Sets whether key tip placement is auto + /// or defined by alignment and margin properties + /// + /// The given element + /// Value + public static void SetAutoPlacement(DependencyObject element, bool value) + { + element.SetValue(AutoPlacementProperty, value); + } + + /// + /// Gets whether key tip placement is auto + /// or defined by alignment and margin properties + /// + /// The given element + [System.ComponentModel.DisplayName("AutoPlacement"), + AttachedPropertyBrowsableForChildren(IncludeDescendants = true), + System.ComponentModel.Category("KeyTips"), + System.ComponentModel.Description("Whether key tip placement is auto or defined by alignment and margin properties")] + public static bool GetAutoPlacement(DependencyObject element) + { + return (bool)element.GetValue(AutoPlacementProperty); + } + + #endregion + + #region HorizontalAlignment Attached Property + + /// + /// Using a DependencyProperty as the backing store for HorizontalAlignment. + /// This enables animation, styling, binding, etc... + /// + public static new readonly DependencyProperty HorizontalAlignmentProperty = DependencyProperty.RegisterAttached( + "HorizontalAlignment", + typeof(HorizontalAlignment), + typeof(KeyTip), + new FrameworkPropertyMetadata(HorizontalAlignment.Center) + ); + + + /// + /// Sets Horizontal Alignment of the key tip + /// + /// The given element + /// Value + public static void SetHorizontalAlignment(DependencyObject element, HorizontalAlignment value) + { + element.SetValue(HorizontalAlignmentProperty, value); + } + + /// + /// Gets Horizontal alignment of the key tip + /// + /// The given element + [System.ComponentModel.DisplayName("HorizontalAlignment"), + AttachedPropertyBrowsableForChildren(IncludeDescendants = true), + System.ComponentModel.Category("KeyTips"), + System.ComponentModel.Description("Horizontal alignment of the key tip")] + public static HorizontalAlignment GetHorizontalAlignment(DependencyObject element) + { + return (HorizontalAlignment)element.GetValue(HorizontalAlignmentProperty); + } + + #endregion + + #region VerticalAlignment Attached Property + + /// + /// Gets vertical alignment of the key tip + /// + /// The given element + [System.ComponentModel.DisplayName("VerticalAlignment"), + AttachedPropertyBrowsableForChildren(IncludeDescendants = true), + System.ComponentModel.Category("KeyTips"), + System.ComponentModel.Description("Vertical alignment of the key tip")] + public static VerticalAlignment GetVerticalAlignment(DependencyObject element) + { + return (VerticalAlignment)element.GetValue(VerticalAlignmentProperty); + } + + /// + /// Sets vertical alignment of the key tip + /// + /// The given element + /// Value + public static void SetVerticalAlignment(DependencyObject obj, VerticalAlignment value) + { + obj.SetValue(VerticalAlignmentProperty, value); + } + + /// + /// Using a DependencyProperty as the backing store for VerticalAlignment. + /// This enables animation, styling, binding, etc... + /// + public static new readonly DependencyProperty VerticalAlignmentProperty = + DependencyProperty.RegisterAttached("VerticalAlignment", + typeof(VerticalAlignment), typeof(KeyTip), + new UIPropertyMetadata(VerticalAlignment.Center)); + + #endregion + + #region Margin Attached Property + + /// + /// Gets margin of the key tip + /// + /// The key tip + /// Margin + [System.ComponentModel.DisplayName("Margin"), + AttachedPropertyBrowsableForChildren(IncludeDescendants = true), + System.ComponentModel.Category("KeyTips"), + System.ComponentModel.Description("Margin of the key tip")] + public static Thickness GetMargin(DependencyObject obj) + { + return (Thickness)obj.GetValue(MarginProperty); + } + + /// + /// Sets margin of the key tip + /// + /// The key tip + /// Value + public static void SetMargin(DependencyObject obj, Thickness value) + { + obj.SetValue(MarginProperty, value); + } + + /// + /// Using a DependencyProperty as the backing store for Margin. + /// This enables animation, styling, binding, etc... + /// + public static new readonly DependencyProperty MarginProperty = + DependencyProperty.RegisterAttached("Margin", typeof(Thickness), typeof(KeyTip), new UIPropertyMetadata(new Thickness())); + + #endregion + + #region Initialization + + // Static constructor + [SuppressMessage("Microsoft.Performance", "CA1810")] + static KeyTip() + { + // Override metadata to allow slyling + //StyleProperty.OverrideMetadata(typeof(KeyTip), new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceStyle))); + DefaultStyleKeyProperty.OverrideMetadata(typeof(KeyTip), new FrameworkPropertyMetadata(typeof(KeyTip))); + StyleProperty.OverrideMetadata(typeof(KeyTip), new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceStyle))); + } + + // Coerce object style + static object OnCoerceStyle(DependencyObject d, object basevalue) + { + if (basevalue == null) + { + basevalue = (d as FrameworkElement).TryFindResource(typeof(KeyTip)); + } + + return basevalue; + } + + /// + /// Default constrctor + /// + public KeyTip() + { + } + + #endregion + } +} diff --git a/Fluent/Controls/MenuItem.cs b/Fluent.Ribbon/Controls/MenuItem.cs similarity index 97% rename from Fluent/Controls/MenuItem.cs rename to Fluent.Ribbon/Controls/MenuItem.cs index 9e1bb03a7..13a791150 100644 --- a/Fluent/Controls/MenuItem.cs +++ b/Fluent.Ribbon/Controls/MenuItem.cs @@ -1,777 +1,777 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Threading; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Controls.Primitives; -using System.Windows.Data; -using System.Windows.Input; -using System.Windows.Markup; - -namespace Fluent -{ - using Fluent.Internal; - - /// - /// Represents menu item - /// - [ContentProperty("Items")] - public class MenuItem : System.Windows.Controls.MenuItem, IQuickAccessItemProvider, IRibbonControl - { - #region Fields - - private Popup popup; - - // Thumb to resize in both directions - Thumb resizeBothThumb; - // Thumb to resize vertical - Thumb resizeVerticalThumb; - - private Panel menuPanel; - - private ScrollViewer scrollViewer; - - #endregion - - #region Properties - - #region Size - - /// - /// Gets or sets Size for the element. - /// - public RibbonControlSize Size - { - get { return (RibbonControlSize)this.GetValue(SizeProperty); } - set { this.SetValue(SizeProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Size. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty SizeProperty = RibbonProperties.SizeProperty.AddOwner(typeof(MenuItem)); - - #endregion - - #region SizeDefinition - - /// - /// Gets or sets SizeDefinition for element. - /// - public RibbonControlSizeDefinition SizeDefinition - { - get { return (RibbonControlSizeDefinition)this.GetValue(SizeDefinitionProperty); } - set { this.SetValue(SizeDefinitionProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for SizeDefinition. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty SizeDefinitionProperty = RibbonProperties.SizeDefinitionProperty.AddOwner(typeof(MenuItem)); - - #endregion - - #region KeyTip - - /// - /// Gets or sets KeyTip for element. - /// - public string KeyTip - { - get { return (string)this.GetValue(KeyTipProperty); } - set { this.SetValue(KeyTipProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Keys. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty KeyTipProperty = Fluent.KeyTip.KeysProperty.AddOwner(typeof(MenuItem)); - - #endregion - - /// - /// Gets drop down popup - /// - public Popup DropDownPopup - { - get { return this.popup; } - } - - /// - /// Gets a value indicating whether context menu is opened - /// - public bool IsContextMenuOpened { get; set; } - - #region Description - - /// - /// Useless property only used in secon level application menu items - /// - public string Description - { - get { return (string)this.GetValue(DescriptionProperty); } - set { this.SetValue(DescriptionProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Description. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty DescriptionProperty = - DependencyProperty.Register("Description", typeof(string), typeof(MenuItem), new UIPropertyMetadata("")); - - - #endregion - - #region IsDropDownOpen - - /// - /// Gets or sets whether popup is opened - /// - public bool IsDropDownOpen - { - get { return this.IsSubmenuOpen; } - set { this.IsSubmenuOpen = value; } - } - - #endregion - - #region IsDefinitive - - /// - /// Gets or sets whether ribbon control click must close backstage - /// - public bool IsDefinitive - { - get { return (bool)this.GetValue(IsDefinitiveProperty); } - set { this.SetValue(IsDefinitiveProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for IsDefinitive. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IsDefinitiveProperty = - DependencyProperty.Register("IsDefinitive", typeof(bool), typeof(MenuItem), new UIPropertyMetadata(true)); - - #endregion - - #region ResizeMode - - /// - /// Gets or sets context menu resize mode - /// - public ContextMenuResizeMode ResizeMode - { - get { return (ContextMenuResizeMode)this.GetValue(ResizeModeProperty); } - set { this.SetValue(ResizeModeProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for ResizeMode. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty ResizeModeProperty = - DependencyProperty.Register("ResizeMode", typeof(ContextMenuResizeMode), - typeof(MenuItem), new UIPropertyMetadata(ContextMenuResizeMode.None)); - - - #endregion - - #region MaxDropDownHeight - - /// - /// Get or sets max height of drop down popup - /// - public double MaxDropDownHeight - { - get { return (double)this.GetValue(MaxDropDownHeightProperty); } - set { this.SetValue(MaxDropDownHeightProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for MaxDropDownHeight. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty MaxDropDownHeightProperty = - DependencyProperty.Register("MaxDropDownHeight", typeof(double), typeof(MenuItem), new UIPropertyMetadata(SystemParameters.PrimaryScreenHeight / 3.0)); - - #endregion - - #region IsSplited - - /// - /// Gets or sets a value indicating whether menu item is splited - /// - public bool IsSplited - { - get { return (bool)this.GetValue(IsSplitedProperty); } - set { this.SetValue(IsSplitedProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for IsSplited. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IsSplitedProperty = - DependencyProperty.Register("IsSplited", typeof(bool), typeof(MenuItem), new UIPropertyMetadata(false)); - - #endregion - - #region GroupName - - /// - /// Gets or sets the name of the group that the toggle button belongs to. - /// Use the GroupName property to specify a grouping of toggle buttons to - /// create a mutually exclusive set of controls. You can use the GroupName - /// property when only one selection is possible from a list of available - /// options. When this property is set, only one ToggleButton in the specified - /// group can be selected at a time. - /// - public string GroupName - { - get { return (string)this.GetValue(GroupNameProperty); } - set { this.SetValue(GroupNameProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for GroupName. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty GroupNameProperty = - DependencyProperty.Register("GroupName", typeof(string), typeof(MenuItem), - new UIPropertyMetadata(null, OnGroupNameChanged)); - - // Group name changed - static void OnGroupNameChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - MenuItem toggleButton = (MenuItem)d; - string currentGroupName = (string)e.NewValue; - string previousGroupName = (string)e.OldValue; - - if (previousGroupName != null) RemoveFromGroup(previousGroupName, toggleButton); - if (currentGroupName != null) AddToGroup(currentGroupName, toggleButton); - } - - #region Grouped Items Methods - - // Grouped buttons (thread id / group name / weak ref to a control) - static readonly Dictionary>> groupedButtons = - new Dictionary>>(); - - // Remove from group - static void RemoveFromGroup(string groupName, MenuItem button) - { - List buttons = null; - int threadId = Thread.CurrentThread.ManagedThreadId; - if (!groupedButtons.ContainsKey(threadId)) return; - if (!groupedButtons[threadId].TryGetValue(groupName, out buttons)) return; - - buttons.RemoveAt(buttons.FindIndex(x => (x.IsAlive && ((MenuItem)x.Target) == button))); - } - - // Remove from group - static void AddToGroup(string groupName, MenuItem button) - { - int threadId = Thread.CurrentThread.ManagedThreadId; - if (!groupedButtons.ContainsKey(threadId)) groupedButtons.Add(threadId, new Dictionary>()); - - List buttons = null; - if (!groupedButtons[threadId].TryGetValue(groupName, out buttons)) - { - buttons = new List(); - groupedButtons[threadId].Add(groupName, buttons); - } - - buttons.Add(new WeakReference(button)); - } - - // Gets all buttons in the given group - static IEnumerable GetItemsInGroup(string groupName) - { - int threadId = Thread.CurrentThread.ManagedThreadId; - if (!groupedButtons.ContainsKey(threadId)) return new List(); - - List buttons = null; - if (!groupedButtons[threadId].TryGetValue(groupName, out buttons)) return new List(); - return buttons.Where(x => x.IsAlive).Select(x => (MenuItem)x.Target); - } - - #endregion - - #region IsChecked - - // Coerce IsChecked - static object CoerceIsChecked(DependencyObject d, object basevalue) - { - MenuItem toggleButton = (MenuItem)d; - if (toggleButton.GroupName == null) return basevalue; - - bool baseIsChecked = (bool)basevalue; - if (!baseIsChecked) - { - // We can not allow that there are no one button checked - foreach (MenuItem item in GetItemsInGroup(toggleButton.GroupName)) - { - // It's Ok, atleast one checked button exists - if (item.IsChecked) return false; - } - - // This button can not be unchecked - return true; - } - return basevalue; - } - - // Handles isChecked changed - private static void OnIsCheckedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - bool newValue = (bool)e.NewValue; - bool oldValue = (bool)e.OldValue; - MenuItem button = (MenuItem)d; - - // Uncheck other toggle buttons - if (newValue && button.GroupName != null) - { - foreach (MenuItem item in GetItemsInGroup(button.GroupName)) - if (item != button) item.IsChecked = false; - } - } - - #endregion - - #endregion - - #endregion - - #region Events - - /// - /// Occurs when context menu is opened - /// - public event EventHandler DropDownOpened; - - /// - /// Occurs when context menu is closed - /// - public event EventHandler DropDownClosed; - - #endregion - - #region Constructors - - /// - /// Static constructor - /// - [SuppressMessage("Microsoft.Performance", "CA1810")] - static MenuItem() - { - Type type = typeof(MenuItem); - ToolTipService.Attach(type); - //PopupService.Attach(type); - ContextMenuService.Attach(type); - StyleProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceStyle))); - IsCheckedProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(false, OnIsCheckedChanged, CoerceIsChecked)); - } - - // Coerce object style - static object OnCoerceStyle(DependencyObject d, object basevalue) - { - if (basevalue == null) - { - basevalue = (d as FrameworkElement).TryFindResource(typeof(MenuItem)); - } - - return basevalue; - } - - /// - /// Default Constructor - /// - public MenuItem() - { - ContextMenuService.Coerce(this); - - this.MouseWheel += this.OnMenuItemMouseWheel; - } - - // Fix to raise MouseWhele event - private void OnMenuItemMouseWheel(object sender, MouseWheelEventArgs e) - { - if (((MenuItem)sender).Parent as ListBox != null) - { - ((ListBox)((MenuItem)sender).Parent).RaiseEvent(e); - } - } - - #endregion - - #region QuickAccess - - /// - /// Gets control which represents shortcut item. - /// This item MUST be synchronized with the original - /// and send command to original one control. - /// - /// Control which represents shortcut item - public FrameworkElement CreateQuickAccessItem() - { - if (this.HasItems) - { - if (this.IsSplited) - { - SplitButton button = new SplitButton(); - RibbonControl.BindQuickAccessItem(this, button); - RibbonControl.Bind(this, button, "ResizeMode", ResizeModeProperty, BindingMode.Default); - RibbonControl.Bind(this, button, "MaxDropDownHeight", MaxDropDownHeightProperty, BindingMode.Default); - RibbonControl.Bind(this, button, "DisplayMemberPath", DisplayMemberPathProperty, BindingMode.OneWay); - RibbonControl.Bind(this, button, "GroupStyleSelector", GroupStyleSelectorProperty, BindingMode.OneWay); - RibbonControl.Bind(this, button, "ItemContainerStyle", ItemContainerStyleProperty, BindingMode.OneWay); - RibbonControl.Bind(this, button, "ItemsPanel", ItemsPanelProperty, BindingMode.OneWay); - RibbonControl.Bind(this, button, "ItemStringFormat", ItemStringFormatProperty, BindingMode.OneWay); - RibbonControl.Bind(this, button, "ItemTemplate", ItemTemplateProperty, BindingMode.OneWay); - button.DropDownOpened += this.OnQuickAccessOpened; - return button; - } - else - { - DropDownButton button = new DropDownButton(); - RibbonControl.BindQuickAccessItem(this, button); - RibbonControl.Bind(this, button, "ResizeMode", ResizeModeProperty, BindingMode.Default); - RibbonControl.Bind(this, button, "MaxDropDownHeight", MaxDropDownHeightProperty, BindingMode.Default); - RibbonControl.Bind(this, button, "DisplayMemberPath", DisplayMemberPathProperty, BindingMode.OneWay); - RibbonControl.Bind(this, button, "GroupStyleSelector", GroupStyleSelectorProperty, BindingMode.OneWay); - RibbonControl.Bind(this, button, "ItemContainerStyle", ItemContainerStyleProperty, BindingMode.OneWay); - RibbonControl.Bind(this, button, "ItemsPanel", ItemsPanelProperty, BindingMode.OneWay); - RibbonControl.Bind(this, button, "ItemStringFormat", ItemStringFormatProperty, BindingMode.OneWay); - RibbonControl.Bind(this, button, "ItemTemplate", ItemTemplateProperty, BindingMode.OneWay); - button.DropDownOpened += this.OnQuickAccessOpened; - return button; - } - } - else - { - Button button = new Button(); - RibbonControl.BindQuickAccessItem(this, button); - return button; - } - } - - /// - /// Handles quick access button drop down menu opened - /// - /// - /// - protected void OnQuickAccessOpened(object sender, EventArgs e) - { - var buttonInQuickAccess = (DropDownButton)sender; - - buttonInQuickAccess.DropDownClosed += this.OnQuickAccessMenuClosedOrUnloaded; - buttonInQuickAccess.Unloaded += this.OnQuickAccessMenuClosedOrUnloaded; - - ItemsControlHelper.MoveItemsToDifferentControl(buttonInQuickAccess, this); - } - - /// - /// Handles quick access button drop down menu closed - /// - /// - /// - protected void OnQuickAccessMenuClosedOrUnloaded(object sender, EventArgs e) - { - var buttonInQuickAccess = (DropDownButton)sender; - - buttonInQuickAccess.DropDownClosed -= this.OnQuickAccessMenuClosedOrUnloaded; - buttonInQuickAccess.Unloaded -= this.OnQuickAccessMenuClosedOrUnloaded; - - ItemsControlHelper.MoveItemsToDifferentControl(buttonInQuickAccess, this); - } - - /// - /// Gets or sets whether control can be added to quick access toolbar - /// - public bool CanAddToQuickAccessToolBar - { - get { return (bool)this.GetValue(CanAddToQuickAccessToolBarProperty); } - set { this.SetValue(CanAddToQuickAccessToolBarProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for CanAddToQuickAccessToolBar. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty CanAddToQuickAccessToolBarProperty = RibbonControl.CanAddToQuickAccessToolBarProperty.AddOwner(typeof(MenuItem)); - - #endregion - - #region Public - - /// - /// Handles key tip pressed - /// - public virtual void OnKeyTipPressed() - { - if (!this.HasItems) - { - this.OnClick(); - } - else - { - Keyboard.Focus(this); - this.IsDropDownOpen = true; - } - } - - /// - /// Handles back navigation with KeyTips - /// - public void OnKeyTipBack() - { - this.IsDropDownOpen = false; - } - - #endregion - - #region Overrides - - /// - /// Creates or identifies the element that is used to display the given item. - /// - /// The element that is used to display the given item. - protected override DependencyObject GetContainerForItemOverride() - { - return new MenuItem(); - } - - /// - /// Determines if the specified item is (or is eligible to be) its own container. - /// - /// The item to check. - /// - protected override bool IsItemItsOwnContainerOverride(object item) - { - return (item is FrameworkElement); - } - - /// - /// Called when the left mouse button is released. - /// - /// The event data for the event. - protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e) - { - if (e.ClickCount == 1) - { - if (this.IsSplited) - { - Border buttonBorder = this.GetTemplateChild("PART_ButtonBorder") as Border; - if ((buttonBorder != null) && (PopupService.IsMousePhysicallyOver(buttonBorder))) - { - /*if (Command != null) - { - RoutedCommand command = Command as RoutedCommand; - if (command != null) command.Execute(CommandParameter, CommandTarget); - else Command.Execute(CommandParameter); - }*/ - this.OnClick(); - } - } - } - base.OnMouseLeftButtonUp(e); - } - - /// - /// Called when a is clicked. - /// - protected override void OnClick() - { - // Close popup on click - if (this.IsDefinitive - && (!this.HasItems || this.IsSplited)) - { - PopupService.RaiseDismissPopupEventAsync(this, DismissPopupMode.Always); - } - - base.OnClick(); - } - - /// - /// Called when the template's tree is generated. - /// - public override void OnApplyTemplate() - { - if (this.popup != null) - { - this.popup.Opened -= this.OnDropDownOpened; - this.popup.Closed -= this.OnDropDownClosed; - } - - this.popup = this.GetTemplateChild("PART_Popup") as Popup; - - if (this.popup != null) - { - this.popup.Opened += this.OnDropDownOpened; - this.popup.Closed += this.OnDropDownClosed; - - KeyboardNavigation.SetControlTabNavigation(this.popup, KeyboardNavigationMode.Cycle); - KeyboardNavigation.SetDirectionalNavigation(this.popup, KeyboardNavigationMode.Cycle); - KeyboardNavigation.SetTabNavigation(this.popup, KeyboardNavigationMode.Cycle); - } - - if (this.resizeVerticalThumb != null) - { - this.resizeVerticalThumb.DragDelta -= this.OnResizeVerticalDelta; - } - this.resizeVerticalThumb = this.GetTemplateChild("PART_ResizeVerticalThumb") as Thumb; - if (this.resizeVerticalThumb != null) - { - this.resizeVerticalThumb.DragDelta += this.OnResizeVerticalDelta; - } - - if (this.resizeBothThumb != null) - { - this.resizeBothThumb.DragDelta -= this.OnResizeBothDelta; - } - this.resizeBothThumb = this.GetTemplateChild("PART_ResizeBothThumb") as Thumb; - if (this.resizeBothThumb != null) - { - this.resizeBothThumb.DragDelta += this.OnResizeBothDelta; - } - this.scrollViewer = this.GetTemplateChild("PART_ScrollViewer") as ScrollViewer; - this.menuPanel = this.GetTemplateChild("PART_MenuPanel") as Panel; - } - - /// - /// Invoked when an unhandled  attached event reaches an element in its route that is derived from this class. Implement this method to add class handling for this event. - /// - /// The that contains the event data. - protected override void OnPreviewLostKeyboardFocus(KeyboardFocusChangedEventArgs e) - { - Debug.WriteLine("MenuItem focus lost - " + this); - //base.OnPreviewLostKeyboardFocus(e); - //e.Handled = true; - } - - /// - /// Responds to the event. - /// - /// The event data for the event. - protected override void OnKeyDown(KeyEventArgs e) - { - if (e.Key == Key.Escape) - { - if (this.IsSubmenuOpen) - this.IsSubmenuOpen = false; - else - { - DependencyObject parent = this.FindParentDropDownOrMenuItem(); - if (parent != null) - { - IDropDownControl dropDown = parent as IDropDownControl; - if (dropDown != null) dropDown.IsDropDownOpen = false; - else (parent as System.Windows.Controls.MenuItem).IsSubmenuOpen = false; - } - } - e.Handled = true; - } - else base.OnKeyDown(e); - } - - private DependencyObject FindParentDropDownOrMenuItem() - { - DependencyObject parent = this.Parent; - while (parent != null) - { - IDropDownControl dropDown = parent as IDropDownControl; - if (dropDown != null) return parent; - System.Windows.Controls.MenuItem menuItem = parent as System.Windows.Controls.MenuItem; - if (menuItem != null) return parent; - parent = LogicalTreeHelper.GetParent(parent); - } - return null; - } - - #endregion - - #region Methods - - // Handles resize both drag - private void OnResizeBothDelta(object sender, DragDeltaEventArgs e) - { - if (this.scrollViewer != null) - { - this.scrollViewer.VerticalScrollBarVisibility = ScrollBarVisibility.Auto; - } - - if (this.menuPanel != null) - { - if (double.IsNaN(this.menuPanel.Width)) - { - this.menuPanel.Width = this.menuPanel.ActualWidth; - } - - if (double.IsNaN(this.menuPanel.Height)) - { - this.menuPanel.Height = this.menuPanel.ActualHeight; - } - - this.menuPanel.Width = Math.Max(this.menuPanel.MinWidth, this.menuPanel.Width + e.HorizontalChange); - this.menuPanel.Height = Math.Min(Math.Max(this.menuPanel.MinHeight, this.menuPanel.Height + e.VerticalChange), this.MaxDropDownHeight); - } - } - - // Handles resize vertical drag - private void OnResizeVerticalDelta(object sender, DragDeltaEventArgs e) - { - if (this.scrollViewer != null) - { - this.scrollViewer.VerticalScrollBarVisibility = ScrollBarVisibility.Auto; - } - - if (this.menuPanel != null) - { - if (double.IsNaN(this.menuPanel.Height)) - { - this.menuPanel.Height = this.menuPanel.ActualHeight; - } - - this.menuPanel.Height = Math.Min(Math.Max(this.menuPanel.MinHeight, this.menuPanel.Height + e.VerticalChange), this.MaxDropDownHeight); - } - } - - // Handles drop down opened - private void OnDropDownClosed(object sender, EventArgs e) - { - if (this.DropDownClosed != null) - { - this.DropDownClosed(this, e); - } - //if (Mouse.Captured == this) Mouse.Capture(null); - } - - // Handles drop down closed - private void OnDropDownOpened(object sender, EventArgs e) - { - if (this.scrollViewer != null - && - this.ResizeMode != ContextMenuResizeMode.None) - { - this.scrollViewer.VerticalScrollBarVisibility = ScrollBarVisibility.Disabled; - } - - if (this.menuPanel != null) - { - this.menuPanel.Width = double.NaN; - this.menuPanel.Height = double.NaN; - } - - if (this.DropDownOpened != null) - { - this.DropDownOpened(this, e); - } - //Mouse.Capture(this, CaptureMode.SubTree); - } - - #endregion - } -} +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Threading; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Data; +using System.Windows.Input; +using System.Windows.Markup; + +namespace Fluent +{ + using Fluent.Internal; + + /// + /// Represents menu item + /// + [ContentProperty("Items")] + public class MenuItem : System.Windows.Controls.MenuItem, IQuickAccessItemProvider, IRibbonControl + { + #region Fields + + private Popup popup; + + // Thumb to resize in both directions + Thumb resizeBothThumb; + // Thumb to resize vertical + Thumb resizeVerticalThumb; + + private Panel menuPanel; + + private ScrollViewer scrollViewer; + + #endregion + + #region Properties + + #region Size + + /// + /// Gets or sets Size for the element. + /// + public RibbonControlSize Size + { + get { return (RibbonControlSize)this.GetValue(SizeProperty); } + set { this.SetValue(SizeProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Size. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty SizeProperty = RibbonProperties.SizeProperty.AddOwner(typeof(MenuItem)); + + #endregion + + #region SizeDefinition + + /// + /// Gets or sets SizeDefinition for element. + /// + public RibbonControlSizeDefinition SizeDefinition + { + get { return (RibbonControlSizeDefinition)this.GetValue(SizeDefinitionProperty); } + set { this.SetValue(SizeDefinitionProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for SizeDefinition. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty SizeDefinitionProperty = RibbonProperties.SizeDefinitionProperty.AddOwner(typeof(MenuItem)); + + #endregion + + #region KeyTip + + /// + /// Gets or sets KeyTip for element. + /// + public string KeyTip + { + get { return (string)this.GetValue(KeyTipProperty); } + set { this.SetValue(KeyTipProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Keys. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty KeyTipProperty = Fluent.KeyTip.KeysProperty.AddOwner(typeof(MenuItem)); + + #endregion + + /// + /// Gets drop down popup + /// + public Popup DropDownPopup + { + get { return this.popup; } + } + + /// + /// Gets a value indicating whether context menu is opened + /// + public bool IsContextMenuOpened { get; set; } + + #region Description + + /// + /// Useless property only used in secon level application menu items + /// + public string Description + { + get { return (string)this.GetValue(DescriptionProperty); } + set { this.SetValue(DescriptionProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Description. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty DescriptionProperty = + DependencyProperty.Register("Description", typeof(string), typeof(MenuItem), new UIPropertyMetadata("")); + + + #endregion + + #region IsDropDownOpen + + /// + /// Gets or sets whether popup is opened + /// + public bool IsDropDownOpen + { + get { return this.IsSubmenuOpen; } + set { this.IsSubmenuOpen = value; } + } + + #endregion + + #region IsDefinitive + + /// + /// Gets or sets whether ribbon control click must close backstage + /// + public bool IsDefinitive + { + get { return (bool)this.GetValue(IsDefinitiveProperty); } + set { this.SetValue(IsDefinitiveProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for IsDefinitive. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IsDefinitiveProperty = + DependencyProperty.Register("IsDefinitive", typeof(bool), typeof(MenuItem), new UIPropertyMetadata(true)); + + #endregion + + #region ResizeMode + + /// + /// Gets or sets context menu resize mode + /// + public ContextMenuResizeMode ResizeMode + { + get { return (ContextMenuResizeMode)this.GetValue(ResizeModeProperty); } + set { this.SetValue(ResizeModeProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for ResizeMode. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty ResizeModeProperty = + DependencyProperty.Register("ResizeMode", typeof(ContextMenuResizeMode), + typeof(MenuItem), new UIPropertyMetadata(ContextMenuResizeMode.None)); + + + #endregion + + #region MaxDropDownHeight + + /// + /// Get or sets max height of drop down popup + /// + public double MaxDropDownHeight + { + get { return (double)this.GetValue(MaxDropDownHeightProperty); } + set { this.SetValue(MaxDropDownHeightProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for MaxDropDownHeight. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty MaxDropDownHeightProperty = + DependencyProperty.Register("MaxDropDownHeight", typeof(double), typeof(MenuItem), new UIPropertyMetadata(SystemParameters.PrimaryScreenHeight / 3.0)); + + #endregion + + #region IsSplited + + /// + /// Gets or sets a value indicating whether menu item is splited + /// + public bool IsSplited + { + get { return (bool)this.GetValue(IsSplitedProperty); } + set { this.SetValue(IsSplitedProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for IsSplited. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IsSplitedProperty = + DependencyProperty.Register("IsSplited", typeof(bool), typeof(MenuItem), new UIPropertyMetadata(false)); + + #endregion + + #region GroupName + + /// + /// Gets or sets the name of the group that the toggle button belongs to. + /// Use the GroupName property to specify a grouping of toggle buttons to + /// create a mutually exclusive set of controls. You can use the GroupName + /// property when only one selection is possible from a list of available + /// options. When this property is set, only one ToggleButton in the specified + /// group can be selected at a time. + /// + public string GroupName + { + get { return (string)this.GetValue(GroupNameProperty); } + set { this.SetValue(GroupNameProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for GroupName. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty GroupNameProperty = + DependencyProperty.Register("GroupName", typeof(string), typeof(MenuItem), + new UIPropertyMetadata(null, OnGroupNameChanged)); + + // Group name changed + static void OnGroupNameChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + MenuItem toggleButton = (MenuItem)d; + string currentGroupName = (string)e.NewValue; + string previousGroupName = (string)e.OldValue; + + if (previousGroupName != null) RemoveFromGroup(previousGroupName, toggleButton); + if (currentGroupName != null) AddToGroup(currentGroupName, toggleButton); + } + + #region Grouped Items Methods + + // Grouped buttons (thread id / group name / weak ref to a control) + static readonly Dictionary>> groupedButtons = + new Dictionary>>(); + + // Remove from group + static void RemoveFromGroup(string groupName, MenuItem button) + { + List buttons = null; + int threadId = Thread.CurrentThread.ManagedThreadId; + if (!groupedButtons.ContainsKey(threadId)) return; + if (!groupedButtons[threadId].TryGetValue(groupName, out buttons)) return; + + buttons.RemoveAt(buttons.FindIndex(x => (x.IsAlive && ((MenuItem)x.Target) == button))); + } + + // Remove from group + static void AddToGroup(string groupName, MenuItem button) + { + int threadId = Thread.CurrentThread.ManagedThreadId; + if (!groupedButtons.ContainsKey(threadId)) groupedButtons.Add(threadId, new Dictionary>()); + + List buttons = null; + if (!groupedButtons[threadId].TryGetValue(groupName, out buttons)) + { + buttons = new List(); + groupedButtons[threadId].Add(groupName, buttons); + } + + buttons.Add(new WeakReference(button)); + } + + // Gets all buttons in the given group + static IEnumerable GetItemsInGroup(string groupName) + { + int threadId = Thread.CurrentThread.ManagedThreadId; + if (!groupedButtons.ContainsKey(threadId)) return new List(); + + List buttons = null; + if (!groupedButtons[threadId].TryGetValue(groupName, out buttons)) return new List(); + return buttons.Where(x => x.IsAlive).Select(x => (MenuItem)x.Target); + } + + #endregion + + #region IsChecked + + // Coerce IsChecked + static object CoerceIsChecked(DependencyObject d, object basevalue) + { + MenuItem toggleButton = (MenuItem)d; + if (toggleButton.GroupName == null) return basevalue; + + bool baseIsChecked = (bool)basevalue; + if (!baseIsChecked) + { + // We can not allow that there are no one button checked + foreach (MenuItem item in GetItemsInGroup(toggleButton.GroupName)) + { + // It's Ok, atleast one checked button exists + if (item.IsChecked) return false; + } + + // This button can not be unchecked + return true; + } + return basevalue; + } + + // Handles isChecked changed + private static void OnIsCheckedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + bool newValue = (bool)e.NewValue; + bool oldValue = (bool)e.OldValue; + MenuItem button = (MenuItem)d; + + // Uncheck other toggle buttons + if (newValue && button.GroupName != null) + { + foreach (MenuItem item in GetItemsInGroup(button.GroupName)) + if (item != button) item.IsChecked = false; + } + } + + #endregion + + #endregion + + #endregion + + #region Events + + /// + /// Occurs when context menu is opened + /// + public event EventHandler DropDownOpened; + + /// + /// Occurs when context menu is closed + /// + public event EventHandler DropDownClosed; + + #endregion + + #region Constructors + + /// + /// Static constructor + /// + [SuppressMessage("Microsoft.Performance", "CA1810")] + static MenuItem() + { + Type type = typeof(MenuItem); + ToolTipService.Attach(type); + //PopupService.Attach(type); + ContextMenuService.Attach(type); + StyleProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceStyle))); + IsCheckedProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(false, OnIsCheckedChanged, CoerceIsChecked)); + } + + // Coerce object style + static object OnCoerceStyle(DependencyObject d, object basevalue) + { + if (basevalue == null) + { + basevalue = (d as FrameworkElement).TryFindResource(typeof(MenuItem)); + } + + return basevalue; + } + + /// + /// Default Constructor + /// + public MenuItem() + { + ContextMenuService.Coerce(this); + + this.MouseWheel += this.OnMenuItemMouseWheel; + } + + // Fix to raise MouseWhele event + private void OnMenuItemMouseWheel(object sender, MouseWheelEventArgs e) + { + if (((MenuItem)sender).Parent as ListBox != null) + { + ((ListBox)((MenuItem)sender).Parent).RaiseEvent(e); + } + } + + #endregion + + #region QuickAccess + + /// + /// Gets control which represents shortcut item. + /// This item MUST be synchronized with the original + /// and send command to original one control. + /// + /// Control which represents shortcut item + public FrameworkElement CreateQuickAccessItem() + { + if (this.HasItems) + { + if (this.IsSplited) + { + SplitButton button = new SplitButton(); + RibbonControl.BindQuickAccessItem(this, button); + RibbonControl.Bind(this, button, "ResizeMode", ResizeModeProperty, BindingMode.Default); + RibbonControl.Bind(this, button, "MaxDropDownHeight", MaxDropDownHeightProperty, BindingMode.Default); + RibbonControl.Bind(this, button, "DisplayMemberPath", DisplayMemberPathProperty, BindingMode.OneWay); + RibbonControl.Bind(this, button, "GroupStyleSelector", GroupStyleSelectorProperty, BindingMode.OneWay); + RibbonControl.Bind(this, button, "ItemContainerStyle", ItemContainerStyleProperty, BindingMode.OneWay); + RibbonControl.Bind(this, button, "ItemsPanel", ItemsPanelProperty, BindingMode.OneWay); + RibbonControl.Bind(this, button, "ItemStringFormat", ItemStringFormatProperty, BindingMode.OneWay); + RibbonControl.Bind(this, button, "ItemTemplate", ItemTemplateProperty, BindingMode.OneWay); + button.DropDownOpened += this.OnQuickAccessOpened; + return button; + } + else + { + DropDownButton button = new DropDownButton(); + RibbonControl.BindQuickAccessItem(this, button); + RibbonControl.Bind(this, button, "ResizeMode", ResizeModeProperty, BindingMode.Default); + RibbonControl.Bind(this, button, "MaxDropDownHeight", MaxDropDownHeightProperty, BindingMode.Default); + RibbonControl.Bind(this, button, "DisplayMemberPath", DisplayMemberPathProperty, BindingMode.OneWay); + RibbonControl.Bind(this, button, "GroupStyleSelector", GroupStyleSelectorProperty, BindingMode.OneWay); + RibbonControl.Bind(this, button, "ItemContainerStyle", ItemContainerStyleProperty, BindingMode.OneWay); + RibbonControl.Bind(this, button, "ItemsPanel", ItemsPanelProperty, BindingMode.OneWay); + RibbonControl.Bind(this, button, "ItemStringFormat", ItemStringFormatProperty, BindingMode.OneWay); + RibbonControl.Bind(this, button, "ItemTemplate", ItemTemplateProperty, BindingMode.OneWay); + button.DropDownOpened += this.OnQuickAccessOpened; + return button; + } + } + else + { + Button button = new Button(); + RibbonControl.BindQuickAccessItem(this, button); + return button; + } + } + + /// + /// Handles quick access button drop down menu opened + /// + /// + /// + protected void OnQuickAccessOpened(object sender, EventArgs e) + { + var buttonInQuickAccess = (DropDownButton)sender; + + buttonInQuickAccess.DropDownClosed += this.OnQuickAccessMenuClosedOrUnloaded; + buttonInQuickAccess.Unloaded += this.OnQuickAccessMenuClosedOrUnloaded; + + ItemsControlHelper.MoveItemsToDifferentControl(buttonInQuickAccess, this); + } + + /// + /// Handles quick access button drop down menu closed + /// + /// + /// + protected void OnQuickAccessMenuClosedOrUnloaded(object sender, EventArgs e) + { + var buttonInQuickAccess = (DropDownButton)sender; + + buttonInQuickAccess.DropDownClosed -= this.OnQuickAccessMenuClosedOrUnloaded; + buttonInQuickAccess.Unloaded -= this.OnQuickAccessMenuClosedOrUnloaded; + + ItemsControlHelper.MoveItemsToDifferentControl(buttonInQuickAccess, this); + } + + /// + /// Gets or sets whether control can be added to quick access toolbar + /// + public bool CanAddToQuickAccessToolBar + { + get { return (bool)this.GetValue(CanAddToQuickAccessToolBarProperty); } + set { this.SetValue(CanAddToQuickAccessToolBarProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for CanAddToQuickAccessToolBar. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty CanAddToQuickAccessToolBarProperty = RibbonControl.CanAddToQuickAccessToolBarProperty.AddOwner(typeof(MenuItem)); + + #endregion + + #region Public + + /// + /// Handles key tip pressed + /// + public virtual void OnKeyTipPressed() + { + if (!this.HasItems) + { + this.OnClick(); + } + else + { + Keyboard.Focus(this); + this.IsDropDownOpen = true; + } + } + + /// + /// Handles back navigation with KeyTips + /// + public void OnKeyTipBack() + { + this.IsDropDownOpen = false; + } + + #endregion + + #region Overrides + + /// + /// Creates or identifies the element that is used to display the given item. + /// + /// The element that is used to display the given item. + protected override DependencyObject GetContainerForItemOverride() + { + return new MenuItem(); + } + + /// + /// Determines if the specified item is (or is eligible to be) its own container. + /// + /// The item to check. + /// + protected override bool IsItemItsOwnContainerOverride(object item) + { + return (item is FrameworkElement); + } + + /// + /// Called when the left mouse button is released. + /// + /// The event data for the event. + protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e) + { + if (e.ClickCount == 1) + { + if (this.IsSplited) + { + Border buttonBorder = this.GetTemplateChild("PART_ButtonBorder") as Border; + if ((buttonBorder != null) && (PopupService.IsMousePhysicallyOver(buttonBorder))) + { + /*if (Command != null) + { + RoutedCommand command = Command as RoutedCommand; + if (command != null) command.Execute(CommandParameter, CommandTarget); + else Command.Execute(CommandParameter); + }*/ + this.OnClick(); + } + } + } + base.OnMouseLeftButtonUp(e); + } + + /// + /// Called when a is clicked. + /// + protected override void OnClick() + { + // Close popup on click + if (this.IsDefinitive + && (!this.HasItems || this.IsSplited)) + { + PopupService.RaiseDismissPopupEventAsync(this, DismissPopupMode.Always); + } + + base.OnClick(); + } + + /// + /// Called when the template's tree is generated. + /// + public override void OnApplyTemplate() + { + if (this.popup != null) + { + this.popup.Opened -= this.OnDropDownOpened; + this.popup.Closed -= this.OnDropDownClosed; + } + + this.popup = this.GetTemplateChild("PART_Popup") as Popup; + + if (this.popup != null) + { + this.popup.Opened += this.OnDropDownOpened; + this.popup.Closed += this.OnDropDownClosed; + + KeyboardNavigation.SetControlTabNavigation(this.popup, KeyboardNavigationMode.Cycle); + KeyboardNavigation.SetDirectionalNavigation(this.popup, KeyboardNavigationMode.Cycle); + KeyboardNavigation.SetTabNavigation(this.popup, KeyboardNavigationMode.Cycle); + } + + if (this.resizeVerticalThumb != null) + { + this.resizeVerticalThumb.DragDelta -= this.OnResizeVerticalDelta; + } + this.resizeVerticalThumb = this.GetTemplateChild("PART_ResizeVerticalThumb") as Thumb; + if (this.resizeVerticalThumb != null) + { + this.resizeVerticalThumb.DragDelta += this.OnResizeVerticalDelta; + } + + if (this.resizeBothThumb != null) + { + this.resizeBothThumb.DragDelta -= this.OnResizeBothDelta; + } + this.resizeBothThumb = this.GetTemplateChild("PART_ResizeBothThumb") as Thumb; + if (this.resizeBothThumb != null) + { + this.resizeBothThumb.DragDelta += this.OnResizeBothDelta; + } + this.scrollViewer = this.GetTemplateChild("PART_ScrollViewer") as ScrollViewer; + this.menuPanel = this.GetTemplateChild("PART_MenuPanel") as Panel; + } + + /// + /// Invoked when an unhandled  attached event reaches an element in its route that is derived from this class. Implement this method to add class handling for this event. + /// + /// The that contains the event data. + protected override void OnPreviewLostKeyboardFocus(KeyboardFocusChangedEventArgs e) + { + Debug.WriteLine("MenuItem focus lost - " + this); + //base.OnPreviewLostKeyboardFocus(e); + //e.Handled = true; + } + + /// + /// Responds to the event. + /// + /// The event data for the event. + protected override void OnKeyDown(KeyEventArgs e) + { + if (e.Key == Key.Escape) + { + if (this.IsSubmenuOpen) + this.IsSubmenuOpen = false; + else + { + DependencyObject parent = this.FindParentDropDownOrMenuItem(); + if (parent != null) + { + IDropDownControl dropDown = parent as IDropDownControl; + if (dropDown != null) dropDown.IsDropDownOpen = false; + else (parent as System.Windows.Controls.MenuItem).IsSubmenuOpen = false; + } + } + e.Handled = true; + } + else base.OnKeyDown(e); + } + + private DependencyObject FindParentDropDownOrMenuItem() + { + DependencyObject parent = this.Parent; + while (parent != null) + { + IDropDownControl dropDown = parent as IDropDownControl; + if (dropDown != null) return parent; + System.Windows.Controls.MenuItem menuItem = parent as System.Windows.Controls.MenuItem; + if (menuItem != null) return parent; + parent = LogicalTreeHelper.GetParent(parent); + } + return null; + } + + #endregion + + #region Methods + + // Handles resize both drag + private void OnResizeBothDelta(object sender, DragDeltaEventArgs e) + { + if (this.scrollViewer != null) + { + this.scrollViewer.VerticalScrollBarVisibility = ScrollBarVisibility.Auto; + } + + if (this.menuPanel != null) + { + if (double.IsNaN(this.menuPanel.Width)) + { + this.menuPanel.Width = this.menuPanel.ActualWidth; + } + + if (double.IsNaN(this.menuPanel.Height)) + { + this.menuPanel.Height = this.menuPanel.ActualHeight; + } + + this.menuPanel.Width = Math.Max(this.menuPanel.MinWidth, this.menuPanel.Width + e.HorizontalChange); + this.menuPanel.Height = Math.Min(Math.Max(this.menuPanel.MinHeight, this.menuPanel.Height + e.VerticalChange), this.MaxDropDownHeight); + } + } + + // Handles resize vertical drag + private void OnResizeVerticalDelta(object sender, DragDeltaEventArgs e) + { + if (this.scrollViewer != null) + { + this.scrollViewer.VerticalScrollBarVisibility = ScrollBarVisibility.Auto; + } + + if (this.menuPanel != null) + { + if (double.IsNaN(this.menuPanel.Height)) + { + this.menuPanel.Height = this.menuPanel.ActualHeight; + } + + this.menuPanel.Height = Math.Min(Math.Max(this.menuPanel.MinHeight, this.menuPanel.Height + e.VerticalChange), this.MaxDropDownHeight); + } + } + + // Handles drop down opened + private void OnDropDownClosed(object sender, EventArgs e) + { + if (this.DropDownClosed != null) + { + this.DropDownClosed(this, e); + } + //if (Mouse.Captured == this) Mouse.Capture(null); + } + + // Handles drop down closed + private void OnDropDownOpened(object sender, EventArgs e) + { + if (this.scrollViewer != null + && + this.ResizeMode != ContextMenuResizeMode.None) + { + this.scrollViewer.VerticalScrollBarVisibility = ScrollBarVisibility.Disabled; + } + + if (this.menuPanel != null) + { + this.menuPanel.Width = double.NaN; + this.menuPanel.Height = double.NaN; + } + + if (this.DropDownOpened != null) + { + this.DropDownOpened(this, e); + } + //Mouse.Capture(this, CaptureMode.SubTree); + } + + #endregion + } +} diff --git a/Fluent/Controls/QuickAccessMenuItem.cs b/Fluent.Ribbon/Controls/QuickAccessMenuItem.cs similarity index 96% rename from Fluent/Controls/QuickAccessMenuItem.cs rename to Fluent.Ribbon/Controls/QuickAccessMenuItem.cs index 71ca52c5f..3b2f7c66b 100644 --- a/Fluent/Controls/QuickAccessMenuItem.cs +++ b/Fluent.Ribbon/Controls/QuickAccessMenuItem.cs @@ -1,287 +1,287 @@ -using System; -using System.Collections; -using System.Diagnostics.CodeAnalysis; -using System.Windows; -using System.Windows.Data; -using System.Windows.Markup; -using System.Windows.Media; -using System.Windows.Controls; - -namespace Fluent -{ - /// - /// This interface must be implemented for controls - /// which are intended to insert to quick access toolbar - /// - public interface IQuickAccessItemProvider - { - /// - /// Gets control which represents shortcut item. - /// This item MUST be syncronized with the original - /// and send command to original one control. - /// - /// Control which represents shortcut item - FrameworkElement CreateQuickAccessItem(); - - /// - /// Gets or sets whether control can be added to quick access toolbar - /// - bool CanAddToQuickAccessToolBar { get; set; } - } - - /// - /// Peresents quick access shortcut to another control - /// - [ContentProperty("Target")] - public class QuickAccessMenuItem : MenuItem - { - #region Fields - - internal Ribbon Ribbon; - - #endregion - - #region Initialization - - [SuppressMessage("Microsoft.Performance", "CA1810")] - static QuickAccessMenuItem() - { - IsCheckableProperty.AddOwner(typeof(QuickAccessMenuItem), new FrameworkPropertyMetadata(true)); - } - - /// - /// Default constructor - /// - public QuickAccessMenuItem() - { - this.Checked += this.OnChecked; - this.Unchecked += this.OnUnchecked; - this.Loaded += this.OnFirstLoaded; - this.Loaded += this.OnItemLoaded; - } - - #endregion - - #region Target Property - - /// - /// Gets or sets shortcut to the target control - /// - public Control Target - { - get { return (Control)this.GetValue(TargetProperty); } - set { this.SetValue(TargetProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for shortcut. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty TargetProperty = - DependencyProperty.Register("Target", typeof(Control), typeof(QuickAccessMenuItem), new UIPropertyMetadata(null,OnTargetChanged)); - - private static void OnTargetChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var quickAccessMenuItem = (QuickAccessMenuItem)d; - var ribbonControl = e.NewValue as IRibbonControl; - - if (quickAccessMenuItem.Header == null - && ribbonControl != null) - { - // Set Default Text Value - RibbonControl.Bind(ribbonControl, quickAccessMenuItem, "Header", HeaderProperty, BindingMode.OneWay); - } - - if (ribbonControl != null) - { - var parent = LogicalTreeHelper.GetParent((DependencyObject)ribbonControl); - if (parent == null) - { - quickAccessMenuItem.AddLogicalChild(ribbonControl); - } - } - - var oldRibbonControl = e.OldValue as IRibbonControl; - - if (oldRibbonControl!=null) - { - var parent = LogicalTreeHelper.GetParent((DependencyObject)oldRibbonControl); - if (parent == quickAccessMenuItem) - { - quickAccessMenuItem.RemoveLogicalChild(oldRibbonControl); - } - } - } - - #endregion - - #region Overrides - - /// - /// Gets an enumerator for logical child elements of this element. - /// - protected override IEnumerator LogicalChildren - { - get - { - if (this.Target != null) - { - var parent = LogicalTreeHelper.GetParent(this.Target); - if (ReferenceEquals(parent, this)) - { - var list = new ArrayList { this.Target }; - return list.GetEnumerator(); - } - } - - return base.LogicalChildren; - } - } - - #endregion - - #region Event Handlers - - private void OnChecked(object sender, RoutedEventArgs e) - { - if (this.Ribbon != null) - { - this.Ribbon.AddToQuickAccessToolBar(this.Target); - } - } - - private void OnUnchecked(object sender, RoutedEventArgs e) - { - if (!this.IsLoaded) - { - return; - } - - if (this.Ribbon != null) - { - this.Ribbon.RemoveFromQuickAccessToolBar(this.Target); - } - } - - private void OnItemLoaded(object sender, RoutedEventArgs e) - { - if (!this.IsLoaded) - { - return; - } - - if (this.Ribbon != null) - { - this.IsChecked = this.Ribbon.IsInQuickAccessToolBar(this.Target); - } - } - - private void OnFirstLoaded(object sender, RoutedEventArgs e) - { - this.Loaded -= this.OnFirstLoaded; - if (this.IsChecked - && this.Ribbon != null) - { - this.Ribbon.AddToQuickAccessToolBar(this.Target); - } - } - - #endregion - } - - /// - /// The class responds to mine controls for QuickAccessToolBar - /// - internal static class QuickAccessItemsProvider - { - #region Public Methods - - /// - /// Determines whether the given control can provide a quick access toolbar item - /// - /// Control - /// True if this control is able to provide - /// a quick access toolbar item, false otherwise - public static bool IsSupported(UIElement element) - { - var provider = element as IQuickAccessItemProvider; - if (provider != null - && provider.CanAddToQuickAccessToolBar) - { - return true; - } - - return false; - } - - /// - /// Gets control which represents quick access toolbar item - /// - /// Host control - /// Control which represents quick access toolbar item - [SuppressMessage("Microsoft.Performance", "CA1800")] - public static FrameworkElement GetQuickAccessItem(UIElement element) - { - FrameworkElement result = null; - - // If control supports the interface just return what it provides - var provider = element as IQuickAccessItemProvider; - if (provider != null - && provider.CanAddToQuickAccessToolBar) - { - result = ((IQuickAccessItemProvider)element).CreateQuickAccessItem(); - } - - // The control isn't supported - if (result == null) - { - throw new ArgumentException("The contol " + element.GetType().Name + " is not able to provide a quick access toolbar item"); - } - - if (BindingOperations.IsDataBound(result, UIElement.VisibilityProperty) == false) - { - RibbonControl.Bind(element, result, "Visibility", UIElement.VisibilityProperty, BindingMode.OneWay); - } - - if (BindingOperations.IsDataBound(result, UIElement.IsEnabledProperty) == false) - { - RibbonControl.Bind(element, result, "IsEnabled", UIElement.IsEnabledProperty, BindingMode.OneWay); - } - - return result; - } - - /// - /// Finds the top supported control - /// - /// Visual - /// Point - /// Point - public static FrameworkElement FindSupportedControl(Visual visual, Point point) - { - var result = VisualTreeHelper.HitTest(visual, point); - if (result == null) - { - return null; - } - - // Try to find in visual (or logical) tree - var element = result.VisualHit as FrameworkElement; - while (element != null) - { - if (IsSupported(element)) - { - return element; - } - - var visualParent = VisualTreeHelper.GetParent(element) as FrameworkElement; - var logicalParent = LogicalTreeHelper.GetParent(element) as FrameworkElement; - element = visualParent ?? logicalParent; - } - - return null; - } - - #endregion - } +using System; +using System.Collections; +using System.Diagnostics.CodeAnalysis; +using System.Windows; +using System.Windows.Data; +using System.Windows.Markup; +using System.Windows.Media; +using System.Windows.Controls; + +namespace Fluent +{ + /// + /// This interface must be implemented for controls + /// which are intended to insert to quick access toolbar + /// + public interface IQuickAccessItemProvider + { + /// + /// Gets control which represents shortcut item. + /// This item MUST be syncronized with the original + /// and send command to original one control. + /// + /// Control which represents shortcut item + FrameworkElement CreateQuickAccessItem(); + + /// + /// Gets or sets whether control can be added to quick access toolbar + /// + bool CanAddToQuickAccessToolBar { get; set; } + } + + /// + /// Peresents quick access shortcut to another control + /// + [ContentProperty("Target")] + public class QuickAccessMenuItem : MenuItem + { + #region Fields + + internal Ribbon Ribbon; + + #endregion + + #region Initialization + + [SuppressMessage("Microsoft.Performance", "CA1810")] + static QuickAccessMenuItem() + { + IsCheckableProperty.AddOwner(typeof(QuickAccessMenuItem), new FrameworkPropertyMetadata(true)); + } + + /// + /// Default constructor + /// + public QuickAccessMenuItem() + { + this.Checked += this.OnChecked; + this.Unchecked += this.OnUnchecked; + this.Loaded += this.OnFirstLoaded; + this.Loaded += this.OnItemLoaded; + } + + #endregion + + #region Target Property + + /// + /// Gets or sets shortcut to the target control + /// + public Control Target + { + get { return (Control)this.GetValue(TargetProperty); } + set { this.SetValue(TargetProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for shortcut. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty TargetProperty = + DependencyProperty.Register("Target", typeof(Control), typeof(QuickAccessMenuItem), new UIPropertyMetadata(null,OnTargetChanged)); + + private static void OnTargetChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var quickAccessMenuItem = (QuickAccessMenuItem)d; + var ribbonControl = e.NewValue as IRibbonControl; + + if (quickAccessMenuItem.Header == null + && ribbonControl != null) + { + // Set Default Text Value + RibbonControl.Bind(ribbonControl, quickAccessMenuItem, "Header", HeaderProperty, BindingMode.OneWay); + } + + if (ribbonControl != null) + { + var parent = LogicalTreeHelper.GetParent((DependencyObject)ribbonControl); + if (parent == null) + { + quickAccessMenuItem.AddLogicalChild(ribbonControl); + } + } + + var oldRibbonControl = e.OldValue as IRibbonControl; + + if (oldRibbonControl!=null) + { + var parent = LogicalTreeHelper.GetParent((DependencyObject)oldRibbonControl); + if (parent == quickAccessMenuItem) + { + quickAccessMenuItem.RemoveLogicalChild(oldRibbonControl); + } + } + } + + #endregion + + #region Overrides + + /// + /// Gets an enumerator for logical child elements of this element. + /// + protected override IEnumerator LogicalChildren + { + get + { + if (this.Target != null) + { + var parent = LogicalTreeHelper.GetParent(this.Target); + if (ReferenceEquals(parent, this)) + { + var list = new ArrayList { this.Target }; + return list.GetEnumerator(); + } + } + + return base.LogicalChildren; + } + } + + #endregion + + #region Event Handlers + + private void OnChecked(object sender, RoutedEventArgs e) + { + if (this.Ribbon != null) + { + this.Ribbon.AddToQuickAccessToolBar(this.Target); + } + } + + private void OnUnchecked(object sender, RoutedEventArgs e) + { + if (!this.IsLoaded) + { + return; + } + + if (this.Ribbon != null) + { + this.Ribbon.RemoveFromQuickAccessToolBar(this.Target); + } + } + + private void OnItemLoaded(object sender, RoutedEventArgs e) + { + if (!this.IsLoaded) + { + return; + } + + if (this.Ribbon != null) + { + this.IsChecked = this.Ribbon.IsInQuickAccessToolBar(this.Target); + } + } + + private void OnFirstLoaded(object sender, RoutedEventArgs e) + { + this.Loaded -= this.OnFirstLoaded; + if (this.IsChecked + && this.Ribbon != null) + { + this.Ribbon.AddToQuickAccessToolBar(this.Target); + } + } + + #endregion + } + + /// + /// The class responds to mine controls for QuickAccessToolBar + /// + internal static class QuickAccessItemsProvider + { + #region Public Methods + + /// + /// Determines whether the given control can provide a quick access toolbar item + /// + /// Control + /// True if this control is able to provide + /// a quick access toolbar item, false otherwise + public static bool IsSupported(UIElement element) + { + var provider = element as IQuickAccessItemProvider; + if (provider != null + && provider.CanAddToQuickAccessToolBar) + { + return true; + } + + return false; + } + + /// + /// Gets control which represents quick access toolbar item + /// + /// Host control + /// Control which represents quick access toolbar item + [SuppressMessage("Microsoft.Performance", "CA1800")] + public static FrameworkElement GetQuickAccessItem(UIElement element) + { + FrameworkElement result = null; + + // If control supports the interface just return what it provides + var provider = element as IQuickAccessItemProvider; + if (provider != null + && provider.CanAddToQuickAccessToolBar) + { + result = ((IQuickAccessItemProvider)element).CreateQuickAccessItem(); + } + + // The control isn't supported + if (result == null) + { + throw new ArgumentException("The contol " + element.GetType().Name + " is not able to provide a quick access toolbar item"); + } + + if (BindingOperations.IsDataBound(result, UIElement.VisibilityProperty) == false) + { + RibbonControl.Bind(element, result, "Visibility", UIElement.VisibilityProperty, BindingMode.OneWay); + } + + if (BindingOperations.IsDataBound(result, UIElement.IsEnabledProperty) == false) + { + RibbonControl.Bind(element, result, "IsEnabled", UIElement.IsEnabledProperty, BindingMode.OneWay); + } + + return result; + } + + /// + /// Finds the top supported control + /// + /// Visual + /// Point + /// Point + public static FrameworkElement FindSupportedControl(Visual visual, Point point) + { + var result = VisualTreeHelper.HitTest(visual, point); + if (result == null) + { + return null; + } + + // Try to find in visual (or logical) tree + var element = result.VisualHit as FrameworkElement; + while (element != null) + { + if (IsSupported(element)) + { + return element; + } + + var visualParent = VisualTreeHelper.GetParent(element) as FrameworkElement; + var logicalParent = LogicalTreeHelper.GetParent(element) as FrameworkElement; + element = visualParent ?? logicalParent; + } + + return null; + } + + #endregion + } } \ No newline at end of file diff --git a/Fluent/Controls/QuickAccessToolBar.cs b/Fluent.Ribbon/Controls/QuickAccessToolBar.cs similarity index 97% rename from Fluent/Controls/QuickAccessToolBar.cs rename to Fluent.Ribbon/Controls/QuickAccessToolBar.cs index 2a5c2284f..6b5fbe046 100644 --- a/Fluent/Controls/QuickAccessToolBar.cs +++ b/Fluent.Ribbon/Controls/QuickAccessToolBar.cs @@ -1,617 +1,617 @@ -namespace Fluent -{ - using System; - using System.Collections; - using System.Collections.ObjectModel; - using System.Collections.Specialized; - using System.Diagnostics.CodeAnalysis; - using System.Globalization; - using System.Linq; - using System.Windows; - using System.Windows.Controls; - using System.Windows.Markup; - using Fluent.Internal; - - /// - /// Represents quick access toolbar - /// - [TemplatePart(Name = "PART_ShowAbove", Type = typeof(MenuItem))] - [TemplatePart(Name = "PART_ShowBelow", Type = typeof(MenuItem))] - [TemplatePart(Name = "PART_MenuPanel", Type = typeof(Panel))] - [TemplatePart(Name = "PART_RootPanel", Type = typeof(Panel))] - [ContentProperty("QuickAccessItems")] - public class QuickAccessToolBar : Control - { - #region Events - - /// - /// Occured when items are added or removed from Quick Access toolbar - /// - public event NotifyCollectionChangedEventHandler ItemsChanged = delegate { }; - - #endregion - - #region Fields - - private DropDownButton toolBarDownButton; - - private DropDownButton menuDownButton; - - // Show above menu item - private MenuItem showAbove; - - // Show below menu item - private MenuItem showBelow; - - // Items of quick access menu - private ObservableCollection quickAccessItems; - - // Root panel - private Panel rootPanel; - - // ToolBar panel - private Panel toolBarPanel; - - // ToolBar overflow panel - private Panel toolBarOverflowPanel; - - // Items of quick access menu - private ObservableCollection items; - - private Size cachedConstraint; - private int cachedNonOverflowItemsCount = -1; - - // Itemc collection was changed - private bool itemsHadChanged; - - private double cachedDeltaWidth; - - #endregion - - #region Properties - - #region Items - - /// - /// Gets items collection - /// - internal ObservableCollection Items - { - get - { - if (this.items == null) - { - this.items = new ObservableCollection(); - this.items.CollectionChanged += this.OnItemsCollectionChanged; - } - - return this.items; - } - } - - private void OnItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) - { - this.cachedNonOverflowItemsCount = this.GetNonOverflowItemsCount(this.DesiredSize.Width); - this.UpdateHasOverflowItems(); - this.itemsHadChanged = true; - this.InvalidateMeasure(); - - this.InvalidateMeasureOfParentRibbon(); - - this.UpdateKeyTips(); - - if (e.OldItems != null) - { - foreach (var item in e.OldItems.OfType()) - { - item.SizeChanged -= this.OnChildSizeChanged; - } - } - - if (e.NewItems != null) - { - foreach (var item in e.NewItems.OfType()) - { - item.SizeChanged += this.OnChildSizeChanged; - } - } - - if (e.Action == NotifyCollectionChangedAction.Reset) - { - foreach (var item in this.Items.OfType()) - { - item.SizeChanged -= this.OnChildSizeChanged; - } - } - - // Raise items changed event - this.ItemsChanged(this, e); - } - - private void OnChildSizeChanged(object sender, SizeChangedEventArgs e) - { - this.InvalidateMeasureOfParentRibbon(); - } - - #endregion - - #region HasOverflowItems - - /// - /// Gets whether QuickAccessToolBar has overflow items - /// - public bool HasOverflowItems - { - get { return (bool)this.GetValue(HasOverflowItemsProperty); } - private set { this.SetValue(HasOverflowItemsPropertyKey, value); } - } - - private static readonly DependencyPropertyKey HasOverflowItemsPropertyKey = - DependencyProperty.RegisterReadOnly("HasOverflowItems", typeof(bool), typeof(QuickAccessToolBar), new UIPropertyMetadata(false)); - - /// - /// Using a DependencyProperty as the backing store for HasOverflowItems. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty HasOverflowItemsProperty = HasOverflowItemsPropertyKey.DependencyProperty; - - #endregion - - #region QuickAccessItems - - /// - /// Gets quick access menu items - /// - public ObservableCollection QuickAccessItems - { - get - { - if (this.quickAccessItems == null) - { - this.quickAccessItems = new ObservableCollection(); - this.quickAccessItems.CollectionChanged += this.OnQuickAccessItemsCollectionChanged; - } - - return this.quickAccessItems; - } - } - - /// - /// Handles quick access menu items chages - /// - /// - /// - private void OnQuickAccessItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) - { - switch (e.Action) - { - case NotifyCollectionChangedAction.Add: - for (var i = 0; i < e.NewItems.Count; i++) - { - if (this.menuDownButton != null) - { - this.menuDownButton.Items.Insert(e.NewStartingIndex + i + 1, e.NewItems[i]); - } - else - { - this.AddLogicalChild(e.NewItems[i]); - } - } - break; - - case NotifyCollectionChangedAction.Remove: - foreach (var item in e.OldItems) - { - if (this.menuDownButton != null) - { - this.menuDownButton.Items.Remove(item); - } - else - { - this.RemoveLogicalChild(item); - } - } - break; - - case NotifyCollectionChangedAction.Replace: - foreach (var item in e.OldItems) - { - if (this.menuDownButton != null) - { - this.menuDownButton.Items.Remove(item); - } - else - { - this.RemoveLogicalChild(item); - } - } - - var ii = 0; - foreach (var item in e.NewItems) - { - if (this.menuDownButton != null) - { - this.menuDownButton.Items.Insert(e.NewStartingIndex + ii + 1, item); - } - else - { - this.AddLogicalChild(item); - } - - ii++; - } - break; - } - } - - #endregion - - #region ShowAboveRibbon - - /// - /// Gets or sets whether quick access toolbar showes above ribbon - /// - public bool ShowAboveRibbon - { - get { return (bool)this.GetValue(ShowAboveRibbonProperty); } - set { this.SetValue(ShowAboveRibbonProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for ShowAboveRibbon. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty ShowAboveRibbonProperty = - DependencyProperty.Register("ShowAboveRibbon", typeof(bool), - typeof(QuickAccessToolBar), new UIPropertyMetadata(true)); - - #endregion - - #region LogicalChildren - - /// - /// Gets an enumerator to the logical child elements - /// - protected override IEnumerator LogicalChildren - { - get - { - yield return this.rootPanel; - } - } - - #endregion - - #region CanQuickAccessLocationChanging - - /// - /// Gets or sets whether user can change location of QAT - /// - public bool CanQuickAccessLocationChanging - { - get { return (bool)this.GetValue(CanQuickAccessLocationChangingProperty); } - set { this.SetValue(CanQuickAccessLocationChangingProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for CanQuickAccessLocationChanging. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty CanQuickAccessLocationChangingProperty = - DependencyProperty.Register("CanQuickAccessLocationChanging", typeof(bool), typeof(QuickAccessToolBar), new UIPropertyMetadata(true)); - - #endregion - - #endregion - - #region Initialization - - /// - /// Static constructor - /// - [SuppressMessage("Microsoft.Performance", "CA1810")] - static QuickAccessToolBar() - { - DefaultStyleKeyProperty.OverrideMetadata(typeof(QuickAccessToolBar), new FrameworkPropertyMetadata(typeof(QuickAccessToolBar))); - StyleProperty.OverrideMetadata(typeof(QuickAccessToolBar), new FrameworkPropertyMetadata(null, OnCoerceStyle)); - } - - // Coerce object style - private static object OnCoerceStyle(DependencyObject d, object basevalue) - { - if (basevalue == null) - { - basevalue = (d as FrameworkElement).TryFindResource(typeof(QuickAccessToolBar)); - } - - return basevalue; - } - - #endregion - - #region Override - - /// - /// When overridden in a derived class, is invoked whenever application code or - /// internal processes call System.Windows.FrameworkElement.ApplyTemplate(). - /// - public override void OnApplyTemplate() - { - if (this.showAbove != null) - { - this.showAbove.Click -= this.OnShowAboveClick; - } - - if (this.showBelow != null) - { - this.showBelow.Click -= this.OnShowBelowClick; - } - - this.showAbove = this.GetTemplateChild("PART_ShowAbove") as MenuItem; - this.showBelow = this.GetTemplateChild("PART_ShowBelow") as MenuItem; - - if (this.showAbove != null) - { - this.showAbove.Click += this.OnShowAboveClick; - } - - if (this.showBelow != null) - { - this.showBelow.Click += this.OnShowBelowClick; - } - - if (this.menuDownButton != null) - { - foreach (var item in this.QuickAccessItems) - { - this.menuDownButton.Items.Remove(item); - item.InvalidateProperty(QuickAccessMenuItem.TargetProperty); - } - } - else if (this.quickAccessItems != null) - { - foreach (var item in this.quickAccessItems) - { - this.RemoveLogicalChild(item); - } - } - - this.menuDownButton = this.GetTemplateChild("PART_MenuDownButton") as DropDownButton; - - if (this.menuDownButton != null - && this.quickAccessItems != null) - { - for (var i = 0; i < this.quickAccessItems.Count; i++) - { - this.menuDownButton.Items.Insert(i + 1, this.quickAccessItems[i]); - this.quickAccessItems[i].InvalidateProperty(QuickAccessMenuItem.TargetProperty); - } - } - - if (this.toolBarDownButton != null) - { - this.toolBarDownButton.DropDownOpened -= this.OnToolBarDownOpened; - this.toolBarDownButton.DropDownClosed -= this.OnToolBarDownClosed; - } - - this.toolBarDownButton = this.GetTemplateChild("PART_ToolbarDownButton") as DropDownButton; - - if (this.toolBarDownButton != null) - { - this.toolBarDownButton.DropDownOpened += this.OnToolBarDownOpened; - this.toolBarDownButton.DropDownClosed += this.OnToolBarDownClosed; - } - - // ToolBar panels - this.toolBarPanel = this.GetTemplateChild("PART_ToolBarPanel") as Panel; - this.toolBarOverflowPanel = this.GetTemplateChild("PART_ToolBarOverflowPanel") as Panel; - - if (this.rootPanel != null) - { - this.RemoveLogicalChild(this.rootPanel); - } - - this.rootPanel = this.GetTemplateChild("PART_RootPanel") as Panel; - - if (this.rootPanel != null) - { - this.AddLogicalChild(this.rootPanel); - } - - // Clears cache - this.cachedDeltaWidth = 0; - this.cachedNonOverflowItemsCount = this.GetNonOverflowItemsCount(this.ActualWidth); - this.cachedConstraint = new Size(); - } - - private void OnToolBarDownClosed(object sender, EventArgs e) - { - this.toolBarOverflowPanel.Children.Clear(); - } - - private void OnToolBarDownOpened(object sender, EventArgs e) - { - if (this.toolBarOverflowPanel.Children.Count > 0) - { - this.toolBarOverflowPanel.Children.Clear(); - } - - for (var i = this.cachedNonOverflowItemsCount; i < this.Items.Count; i++) - { - this.toolBarOverflowPanel.Children.Add(this.Items[i]); - } - } - - /// - /// Handles show below menu item click - /// - /// Sender - /// The event data - private void OnShowBelowClick(object sender, RoutedEventArgs e) - { - this.ShowAboveRibbon = false; - } - - /// - /// Handles show above menu item click - /// - /// Sender - /// The event data - private void OnShowAboveClick(object sender, RoutedEventArgs e) - { - this.ShowAboveRibbon = true; - } - - /// - /// Called to remeasure a control. - /// - /// The size of the control, up to the maximum specified by constraint - /// The maximum size that the method can return - protected override Size MeasureOverride(Size constraint) - { - if ((this.cachedConstraint == constraint) - && !this.itemsHadChanged) - { - return base.MeasureOverride(constraint); - } - - var nonOverflowItemsCount = this.GetNonOverflowItemsCount(constraint.Width); - - if (this.itemsHadChanged == false - && nonOverflowItemsCount == this.cachedNonOverflowItemsCount) - { - return base.MeasureOverride(constraint); - } - - this.cachedNonOverflowItemsCount = nonOverflowItemsCount; - this.UpdateHasOverflowItems(); - this.cachedConstraint = constraint; - - if (this.HasOverflowItems == false) - { - this.toolBarOverflowPanel.Children.Clear(); - } - - if (this.itemsHadChanged) - { - // Refill toolbar - this.toolBarPanel.Children.Clear(); - - for (var i = 0; i < this.cachedNonOverflowItemsCount; i++) - { - this.toolBarPanel.Children.Add(this.Items[i]); - } - - this.itemsHadChanged = false; - } - else - { - if (this.cachedNonOverflowItemsCount > this.toolBarPanel.Children.Count) - { - // Add needed items - var savedCount = this.toolBarPanel.Children.Count; - for (var i = savedCount; i < this.cachedNonOverflowItemsCount; i++) - { - this.toolBarPanel.Children.Add(this.Items[i]); - } - } - else - { - // Remove nonneeded items - for (var i = this.toolBarPanel.Children.Count - 1; i >= this.cachedNonOverflowItemsCount; i--) - { - this.toolBarPanel.Children.Remove(this.Items[i]); - } - } - } - - return base.MeasureOverride(constraint); - } - - /// - /// We have to use this function because setting a very frequently is quite expensive - /// - private void UpdateHasOverflowItems() - { - var newValue = this.cachedNonOverflowItemsCount < this.Items.Count; - - // ReSharper disable RedundantCheckBeforeAssignment - if (this.HasOverflowItems != newValue) - // ReSharper restore RedundantCheckBeforeAssignment - { - this.HasOverflowItems = newValue; - } - } - - #endregion - - #region Methods - - /// - /// First calls and then - /// - public void Refresh() - { - this.InvalidateMeasure(); - this.InvalidateMeasureOfParentRibbon(); - } - - private void InvalidateMeasureOfParentRibbon() - { - var parentRibbon = this.Parent as Ribbon; - - if (parentRibbon != null) - { - parentRibbon.TitleBar.InvalidateMeasure(); - } - } - - // Updates keys for keytip access - private void UpdateKeyTips() - { - for (var i = 0; i < Math.Min(9, this.Items.Count); i++) - { - // 1, 2, 3, ... , 9 - KeyTip.SetKeys(this.Items[i], (i + 1).ToString(CultureInfo.InvariantCulture)); - } - - for (var i = 9; i < Math.Min(18, this.Items.Count); i++) - { - // 09, 08, 07, ... , 01 - KeyTip.SetKeys(this.Items[i], "0" + (18 - i).ToString(CultureInfo.InvariantCulture)); - } - - var startChar = 'A'; - for (var i = 18; i < Math.Min(9 + 9 + 26, this.Items.Count); i++) - { - // 0A, 0B, 0C, ... , 0Z - KeyTip.SetKeys(this.Items[i], "0" + startChar++); - } - } - - private int GetNonOverflowItemsCount(double width) - { - if (DoubleUtil.AreClose(this.cachedDeltaWidth, 0) - && this.rootPanel != null - && this.toolBarPanel != null) - { - this.rootPanel.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); - this.cachedDeltaWidth = this.rootPanel.DesiredSize.Width - this.toolBarPanel.DesiredSize.Width; - } - - var currentWidth = 0D; - for (var i = 0; i < this.Items.Count; i++) - { - this.Items[i].Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); - currentWidth += this.Items[i].DesiredSize.Width; - - if (currentWidth + this.cachedDeltaWidth > width) - { - return i; - } - } - - return this.Items.Count; - } - - #endregion - } +namespace Fluent +{ + using System; + using System.Collections; + using System.Collections.ObjectModel; + using System.Collections.Specialized; + using System.Diagnostics.CodeAnalysis; + using System.Globalization; + using System.Linq; + using System.Windows; + using System.Windows.Controls; + using System.Windows.Markup; + using Fluent.Internal; + + /// + /// Represents quick access toolbar + /// + [TemplatePart(Name = "PART_ShowAbove", Type = typeof(MenuItem))] + [TemplatePart(Name = "PART_ShowBelow", Type = typeof(MenuItem))] + [TemplatePart(Name = "PART_MenuPanel", Type = typeof(Panel))] + [TemplatePart(Name = "PART_RootPanel", Type = typeof(Panel))] + [ContentProperty("QuickAccessItems")] + public class QuickAccessToolBar : Control + { + #region Events + + /// + /// Occured when items are added or removed from Quick Access toolbar + /// + public event NotifyCollectionChangedEventHandler ItemsChanged = delegate { }; + + #endregion + + #region Fields + + private DropDownButton toolBarDownButton; + + private DropDownButton menuDownButton; + + // Show above menu item + private MenuItem showAbove; + + // Show below menu item + private MenuItem showBelow; + + // Items of quick access menu + private ObservableCollection quickAccessItems; + + // Root panel + private Panel rootPanel; + + // ToolBar panel + private Panel toolBarPanel; + + // ToolBar overflow panel + private Panel toolBarOverflowPanel; + + // Items of quick access menu + private ObservableCollection items; + + private Size cachedConstraint; + private int cachedNonOverflowItemsCount = -1; + + // Itemc collection was changed + private bool itemsHadChanged; + + private double cachedDeltaWidth; + + #endregion + + #region Properties + + #region Items + + /// + /// Gets items collection + /// + internal ObservableCollection Items + { + get + { + if (this.items == null) + { + this.items = new ObservableCollection(); + this.items.CollectionChanged += this.OnItemsCollectionChanged; + } + + return this.items; + } + } + + private void OnItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + this.cachedNonOverflowItemsCount = this.GetNonOverflowItemsCount(this.DesiredSize.Width); + this.UpdateHasOverflowItems(); + this.itemsHadChanged = true; + this.InvalidateMeasure(); + + this.InvalidateMeasureOfParentRibbon(); + + this.UpdateKeyTips(); + + if (e.OldItems != null) + { + foreach (var item in e.OldItems.OfType()) + { + item.SizeChanged -= this.OnChildSizeChanged; + } + } + + if (e.NewItems != null) + { + foreach (var item in e.NewItems.OfType()) + { + item.SizeChanged += this.OnChildSizeChanged; + } + } + + if (e.Action == NotifyCollectionChangedAction.Reset) + { + foreach (var item in this.Items.OfType()) + { + item.SizeChanged -= this.OnChildSizeChanged; + } + } + + // Raise items changed event + this.ItemsChanged(this, e); + } + + private void OnChildSizeChanged(object sender, SizeChangedEventArgs e) + { + this.InvalidateMeasureOfParentRibbon(); + } + + #endregion + + #region HasOverflowItems + + /// + /// Gets whether QuickAccessToolBar has overflow items + /// + public bool HasOverflowItems + { + get { return (bool)this.GetValue(HasOverflowItemsProperty); } + private set { this.SetValue(HasOverflowItemsPropertyKey, value); } + } + + private static readonly DependencyPropertyKey HasOverflowItemsPropertyKey = + DependencyProperty.RegisterReadOnly("HasOverflowItems", typeof(bool), typeof(QuickAccessToolBar), new UIPropertyMetadata(false)); + + /// + /// Using a DependencyProperty as the backing store for HasOverflowItems. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty HasOverflowItemsProperty = HasOverflowItemsPropertyKey.DependencyProperty; + + #endregion + + #region QuickAccessItems + + /// + /// Gets quick access menu items + /// + public ObservableCollection QuickAccessItems + { + get + { + if (this.quickAccessItems == null) + { + this.quickAccessItems = new ObservableCollection(); + this.quickAccessItems.CollectionChanged += this.OnQuickAccessItemsCollectionChanged; + } + + return this.quickAccessItems; + } + } + + /// + /// Handles quick access menu items chages + /// + /// + /// + private void OnQuickAccessItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + for (var i = 0; i < e.NewItems.Count; i++) + { + if (this.menuDownButton != null) + { + this.menuDownButton.Items.Insert(e.NewStartingIndex + i + 1, e.NewItems[i]); + } + else + { + this.AddLogicalChild(e.NewItems[i]); + } + } + break; + + case NotifyCollectionChangedAction.Remove: + foreach (var item in e.OldItems) + { + if (this.menuDownButton != null) + { + this.menuDownButton.Items.Remove(item); + } + else + { + this.RemoveLogicalChild(item); + } + } + break; + + case NotifyCollectionChangedAction.Replace: + foreach (var item in e.OldItems) + { + if (this.menuDownButton != null) + { + this.menuDownButton.Items.Remove(item); + } + else + { + this.RemoveLogicalChild(item); + } + } + + var ii = 0; + foreach (var item in e.NewItems) + { + if (this.menuDownButton != null) + { + this.menuDownButton.Items.Insert(e.NewStartingIndex + ii + 1, item); + } + else + { + this.AddLogicalChild(item); + } + + ii++; + } + break; + } + } + + #endregion + + #region ShowAboveRibbon + + /// + /// Gets or sets whether quick access toolbar showes above ribbon + /// + public bool ShowAboveRibbon + { + get { return (bool)this.GetValue(ShowAboveRibbonProperty); } + set { this.SetValue(ShowAboveRibbonProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for ShowAboveRibbon. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty ShowAboveRibbonProperty = + DependencyProperty.Register("ShowAboveRibbon", typeof(bool), + typeof(QuickAccessToolBar), new UIPropertyMetadata(true)); + + #endregion + + #region LogicalChildren + + /// + /// Gets an enumerator to the logical child elements + /// + protected override IEnumerator LogicalChildren + { + get + { + yield return this.rootPanel; + } + } + + #endregion + + #region CanQuickAccessLocationChanging + + /// + /// Gets or sets whether user can change location of QAT + /// + public bool CanQuickAccessLocationChanging + { + get { return (bool)this.GetValue(CanQuickAccessLocationChangingProperty); } + set { this.SetValue(CanQuickAccessLocationChangingProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for CanQuickAccessLocationChanging. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty CanQuickAccessLocationChangingProperty = + DependencyProperty.Register("CanQuickAccessLocationChanging", typeof(bool), typeof(QuickAccessToolBar), new UIPropertyMetadata(true)); + + #endregion + + #endregion + + #region Initialization + + /// + /// Static constructor + /// + [SuppressMessage("Microsoft.Performance", "CA1810")] + static QuickAccessToolBar() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(QuickAccessToolBar), new FrameworkPropertyMetadata(typeof(QuickAccessToolBar))); + StyleProperty.OverrideMetadata(typeof(QuickAccessToolBar), new FrameworkPropertyMetadata(null, OnCoerceStyle)); + } + + // Coerce object style + private static object OnCoerceStyle(DependencyObject d, object basevalue) + { + if (basevalue == null) + { + basevalue = (d as FrameworkElement).TryFindResource(typeof(QuickAccessToolBar)); + } + + return basevalue; + } + + #endregion + + #region Override + + /// + /// When overridden in a derived class, is invoked whenever application code or + /// internal processes call System.Windows.FrameworkElement.ApplyTemplate(). + /// + public override void OnApplyTemplate() + { + if (this.showAbove != null) + { + this.showAbove.Click -= this.OnShowAboveClick; + } + + if (this.showBelow != null) + { + this.showBelow.Click -= this.OnShowBelowClick; + } + + this.showAbove = this.GetTemplateChild("PART_ShowAbove") as MenuItem; + this.showBelow = this.GetTemplateChild("PART_ShowBelow") as MenuItem; + + if (this.showAbove != null) + { + this.showAbove.Click += this.OnShowAboveClick; + } + + if (this.showBelow != null) + { + this.showBelow.Click += this.OnShowBelowClick; + } + + if (this.menuDownButton != null) + { + foreach (var item in this.QuickAccessItems) + { + this.menuDownButton.Items.Remove(item); + item.InvalidateProperty(QuickAccessMenuItem.TargetProperty); + } + } + else if (this.quickAccessItems != null) + { + foreach (var item in this.quickAccessItems) + { + this.RemoveLogicalChild(item); + } + } + + this.menuDownButton = this.GetTemplateChild("PART_MenuDownButton") as DropDownButton; + + if (this.menuDownButton != null + && this.quickAccessItems != null) + { + for (var i = 0; i < this.quickAccessItems.Count; i++) + { + this.menuDownButton.Items.Insert(i + 1, this.quickAccessItems[i]); + this.quickAccessItems[i].InvalidateProperty(QuickAccessMenuItem.TargetProperty); + } + } + + if (this.toolBarDownButton != null) + { + this.toolBarDownButton.DropDownOpened -= this.OnToolBarDownOpened; + this.toolBarDownButton.DropDownClosed -= this.OnToolBarDownClosed; + } + + this.toolBarDownButton = this.GetTemplateChild("PART_ToolbarDownButton") as DropDownButton; + + if (this.toolBarDownButton != null) + { + this.toolBarDownButton.DropDownOpened += this.OnToolBarDownOpened; + this.toolBarDownButton.DropDownClosed += this.OnToolBarDownClosed; + } + + // ToolBar panels + this.toolBarPanel = this.GetTemplateChild("PART_ToolBarPanel") as Panel; + this.toolBarOverflowPanel = this.GetTemplateChild("PART_ToolBarOverflowPanel") as Panel; + + if (this.rootPanel != null) + { + this.RemoveLogicalChild(this.rootPanel); + } + + this.rootPanel = this.GetTemplateChild("PART_RootPanel") as Panel; + + if (this.rootPanel != null) + { + this.AddLogicalChild(this.rootPanel); + } + + // Clears cache + this.cachedDeltaWidth = 0; + this.cachedNonOverflowItemsCount = this.GetNonOverflowItemsCount(this.ActualWidth); + this.cachedConstraint = new Size(); + } + + private void OnToolBarDownClosed(object sender, EventArgs e) + { + this.toolBarOverflowPanel.Children.Clear(); + } + + private void OnToolBarDownOpened(object sender, EventArgs e) + { + if (this.toolBarOverflowPanel.Children.Count > 0) + { + this.toolBarOverflowPanel.Children.Clear(); + } + + for (var i = this.cachedNonOverflowItemsCount; i < this.Items.Count; i++) + { + this.toolBarOverflowPanel.Children.Add(this.Items[i]); + } + } + + /// + /// Handles show below menu item click + /// + /// Sender + /// The event data + private void OnShowBelowClick(object sender, RoutedEventArgs e) + { + this.ShowAboveRibbon = false; + } + + /// + /// Handles show above menu item click + /// + /// Sender + /// The event data + private void OnShowAboveClick(object sender, RoutedEventArgs e) + { + this.ShowAboveRibbon = true; + } + + /// + /// Called to remeasure a control. + /// + /// The size of the control, up to the maximum specified by constraint + /// The maximum size that the method can return + protected override Size MeasureOverride(Size constraint) + { + if ((this.cachedConstraint == constraint) + && !this.itemsHadChanged) + { + return base.MeasureOverride(constraint); + } + + var nonOverflowItemsCount = this.GetNonOverflowItemsCount(constraint.Width); + + if (this.itemsHadChanged == false + && nonOverflowItemsCount == this.cachedNonOverflowItemsCount) + { + return base.MeasureOverride(constraint); + } + + this.cachedNonOverflowItemsCount = nonOverflowItemsCount; + this.UpdateHasOverflowItems(); + this.cachedConstraint = constraint; + + if (this.HasOverflowItems == false) + { + this.toolBarOverflowPanel.Children.Clear(); + } + + if (this.itemsHadChanged) + { + // Refill toolbar + this.toolBarPanel.Children.Clear(); + + for (var i = 0; i < this.cachedNonOverflowItemsCount; i++) + { + this.toolBarPanel.Children.Add(this.Items[i]); + } + + this.itemsHadChanged = false; + } + else + { + if (this.cachedNonOverflowItemsCount > this.toolBarPanel.Children.Count) + { + // Add needed items + var savedCount = this.toolBarPanel.Children.Count; + for (var i = savedCount; i < this.cachedNonOverflowItemsCount; i++) + { + this.toolBarPanel.Children.Add(this.Items[i]); + } + } + else + { + // Remove nonneeded items + for (var i = this.toolBarPanel.Children.Count - 1; i >= this.cachedNonOverflowItemsCount; i--) + { + this.toolBarPanel.Children.Remove(this.Items[i]); + } + } + } + + return base.MeasureOverride(constraint); + } + + /// + /// We have to use this function because setting a very frequently is quite expensive + /// + private void UpdateHasOverflowItems() + { + var newValue = this.cachedNonOverflowItemsCount < this.Items.Count; + + // ReSharper disable RedundantCheckBeforeAssignment + if (this.HasOverflowItems != newValue) + // ReSharper restore RedundantCheckBeforeAssignment + { + this.HasOverflowItems = newValue; + } + } + + #endregion + + #region Methods + + /// + /// First calls and then + /// + public void Refresh() + { + this.InvalidateMeasure(); + this.InvalidateMeasureOfParentRibbon(); + } + + private void InvalidateMeasureOfParentRibbon() + { + var parentRibbon = this.Parent as Ribbon; + + if (parentRibbon != null) + { + parentRibbon.TitleBar.InvalidateMeasure(); + } + } + + // Updates keys for keytip access + private void UpdateKeyTips() + { + for (var i = 0; i < Math.Min(9, this.Items.Count); i++) + { + // 1, 2, 3, ... , 9 + KeyTip.SetKeys(this.Items[i], (i + 1).ToString(CultureInfo.InvariantCulture)); + } + + for (var i = 9; i < Math.Min(18, this.Items.Count); i++) + { + // 09, 08, 07, ... , 01 + KeyTip.SetKeys(this.Items[i], "0" + (18 - i).ToString(CultureInfo.InvariantCulture)); + } + + var startChar = 'A'; + for (var i = 18; i < Math.Min(9 + 9 + 26, this.Items.Count); i++) + { + // 0A, 0B, 0C, ... , 0Z + KeyTip.SetKeys(this.Items[i], "0" + startChar++); + } + } + + private int GetNonOverflowItemsCount(double width) + { + if (DoubleUtil.AreClose(this.cachedDeltaWidth, 0) + && this.rootPanel != null + && this.toolBarPanel != null) + { + this.rootPanel.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); + this.cachedDeltaWidth = this.rootPanel.DesiredSize.Width - this.toolBarPanel.DesiredSize.Width; + } + + var currentWidth = 0D; + for (var i = 0; i < this.Items.Count; i++) + { + this.Items[i].Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); + currentWidth += this.Items[i].DesiredSize.Width; + + if (currentWidth + this.cachedDeltaWidth > width) + { + return i; + } + } + + return this.Items.Count; + } + + #endregion + } } \ No newline at end of file diff --git a/Fluent/Controls/RadioButton.cs b/Fluent.Ribbon/Controls/RadioButton.cs similarity index 97% rename from Fluent/Controls/RadioButton.cs rename to Fluent.Ribbon/Controls/RadioButton.cs index 18bf9f085..85bd66827 100644 --- a/Fluent/Controls/RadioButton.cs +++ b/Fluent.Ribbon/Controls/RadioButton.cs @@ -1,235 +1,235 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.Windows; -using System.Windows.Data; -using System.Windows.Markup; -using System.Windows.Media; - -namespace Fluent -{ - /// - /// Represents Fluent UI specific RadioButton - /// - [ContentProperty("Header")] - public class RadioButton : System.Windows.Controls.RadioButton, IRibbonControl, IQuickAccessItemProvider - { - #region Properties - - #region Size - - /// - /// Gets or sets Size for the element. - /// - public RibbonControlSize Size - { - get { return (RibbonControlSize)this.GetValue(SizeProperty); } - set { this.SetValue(SizeProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Size. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty SizeProperty = RibbonProperties.SizeProperty.AddOwner(typeof(RadioButton)); - - #endregion - - #region SizeDefinition - - /// - /// Gets or sets SizeDefinition for element. - /// - public RibbonControlSizeDefinition SizeDefinition - { - get { return (RibbonControlSizeDefinition)this.GetValue(SizeDefinitionProperty); } - set { this.SetValue(SizeDefinitionProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for SizeDefinition. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty SizeDefinitionProperty = RibbonProperties.SizeDefinitionProperty.AddOwner(typeof(RadioButton)); - - #endregion - - #region KeyTip - - /// - /// Gets or sets KeyTip for element. - /// - public string KeyTip - { - get { return (string)this.GetValue(KeyTipProperty); } - set { this.SetValue(KeyTipProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Keys. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty KeyTipProperty = Fluent.KeyTip.KeysProperty.AddOwner(typeof(RadioButton)); - - #endregion - - #region Header - - /// - /// Gets or sets element Text - /// - public object Header - { - get { return (string)this.GetValue(HeaderProperty); } - set { this.SetValue(HeaderProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Header. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty HeaderProperty = RibbonControl.HeaderProperty.AddOwner(typeof(RadioButton)); - - #endregion - - #region Icon - - /// - /// Gets or sets Icon for the element - /// - public object Icon - { - get { return (ImageSource)this.GetValue(IconProperty); } - set { this.SetValue(IconProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Icon. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IconProperty = RibbonControl.IconProperty.AddOwner(typeof(RadioButton), new UIPropertyMetadata(null, OnIconChanged)); - - private static void OnIconChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - RadioButton element = d as RadioButton; - FrameworkElement oldElement = e.OldValue as FrameworkElement; - if (oldElement != null) element.RemoveLogicalChild(oldElement); - FrameworkElement newElement = e.NewValue as FrameworkElement; - if (newElement != null) element.AddLogicalChild(newElement); - } - - #endregion - - #region LargeIcon - - /// - /// Gets or sets button large icon - /// - public ImageSource LargeIcon - { - get { return (ImageSource)this.GetValue(LargeIconProperty); } - set { this.SetValue(LargeIconProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for SmallIcon. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty LargeIconProperty = - DependencyProperty.Register("LargeIcon", typeof(ImageSource), - typeof(RadioButton), new UIPropertyMetadata(null)); - - #endregion - - #endregion - - #region Constructors - - /// - /// Static constructor - /// - [SuppressMessage("Microsoft.Performance", "CA1810")] - static RadioButton() - { - Type type = typeof(RadioButton); - DefaultStyleKeyProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(type)); - ContextMenuService.Attach(type); - ToolTipService.Attach(type); - StyleProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceStyle))); - } - - // Coerce object style - static object OnCoerceStyle(DependencyObject d, object basevalue) - { - if (basevalue == null) - { - //basevalue = (d as FrameworkElement).TryFindResource(typeof(QuickAccessToolBar)); - basevalue = (d as FrameworkElement).TryFindResource(typeof(RadioButton)); - } - - return basevalue; - } - - /// - /// Default constructor - /// - public RadioButton() - { - ContextMenuService.Coerce(this); - } - - #endregion - - #region Quick Access Item Creating - - /// - /// Gets control which represents shortcut item. - /// This item MUST be syncronized with the original - /// and send command to original one control. - /// - /// Control which represents shortcut item - public virtual FrameworkElement CreateQuickAccessItem() - { - RadioButton button = new RadioButton(); - - RibbonControl.Bind(this, button, "IsChecked", IsCheckedProperty, BindingMode.TwoWay); - button.Click += ((sender, e) => this.RaiseEvent(e)); - RibbonControl.BindQuickAccessItem(this, button); - - return button; - } - - /// - /// Gets or sets whether control can be added to quick access toolbar - /// - public bool CanAddToQuickAccessToolBar - { - get { return (bool)this.GetValue(CanAddToQuickAccessToolBarProperty); } - set { this.SetValue(CanAddToQuickAccessToolBarProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for CanAddToQuickAccessToolBar. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty CanAddToQuickAccessToolBarProperty = RibbonControl.CanAddToQuickAccessToolBarProperty.AddOwner(typeof(RadioButton), new UIPropertyMetadata(true, RibbonControl.OnCanAddToQuickAccessToolbarChanged)); - - #endregion - - #region Implementation of IKeyTipedControl - - /// - /// Handles key tip pressed - /// - public void OnKeyTipPressed() - { - this.OnClick(); - } - - /// - /// Handles back navigation with KeyTips - /// - public void OnKeyTipBack() - { - } - - #endregion - } +using System; +using System.Diagnostics.CodeAnalysis; +using System.Windows; +using System.Windows.Data; +using System.Windows.Markup; +using System.Windows.Media; + +namespace Fluent +{ + /// + /// Represents Fluent UI specific RadioButton + /// + [ContentProperty("Header")] + public class RadioButton : System.Windows.Controls.RadioButton, IRibbonControl, IQuickAccessItemProvider + { + #region Properties + + #region Size + + /// + /// Gets or sets Size for the element. + /// + public RibbonControlSize Size + { + get { return (RibbonControlSize)this.GetValue(SizeProperty); } + set { this.SetValue(SizeProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Size. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty SizeProperty = RibbonProperties.SizeProperty.AddOwner(typeof(RadioButton)); + + #endregion + + #region SizeDefinition + + /// + /// Gets or sets SizeDefinition for element. + /// + public RibbonControlSizeDefinition SizeDefinition + { + get { return (RibbonControlSizeDefinition)this.GetValue(SizeDefinitionProperty); } + set { this.SetValue(SizeDefinitionProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for SizeDefinition. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty SizeDefinitionProperty = RibbonProperties.SizeDefinitionProperty.AddOwner(typeof(RadioButton)); + + #endregion + + #region KeyTip + + /// + /// Gets or sets KeyTip for element. + /// + public string KeyTip + { + get { return (string)this.GetValue(KeyTipProperty); } + set { this.SetValue(KeyTipProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Keys. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty KeyTipProperty = Fluent.KeyTip.KeysProperty.AddOwner(typeof(RadioButton)); + + #endregion + + #region Header + + /// + /// Gets or sets element Text + /// + public object Header + { + get { return (string)this.GetValue(HeaderProperty); } + set { this.SetValue(HeaderProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Header. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty HeaderProperty = RibbonControl.HeaderProperty.AddOwner(typeof(RadioButton)); + + #endregion + + #region Icon + + /// + /// Gets or sets Icon for the element + /// + public object Icon + { + get { return (ImageSource)this.GetValue(IconProperty); } + set { this.SetValue(IconProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Icon. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IconProperty = RibbonControl.IconProperty.AddOwner(typeof(RadioButton), new UIPropertyMetadata(null, OnIconChanged)); + + private static void OnIconChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + RadioButton element = d as RadioButton; + FrameworkElement oldElement = e.OldValue as FrameworkElement; + if (oldElement != null) element.RemoveLogicalChild(oldElement); + FrameworkElement newElement = e.NewValue as FrameworkElement; + if (newElement != null) element.AddLogicalChild(newElement); + } + + #endregion + + #region LargeIcon + + /// + /// Gets or sets button large icon + /// + public ImageSource LargeIcon + { + get { return (ImageSource)this.GetValue(LargeIconProperty); } + set { this.SetValue(LargeIconProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for SmallIcon. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty LargeIconProperty = + DependencyProperty.Register("LargeIcon", typeof(ImageSource), + typeof(RadioButton), new UIPropertyMetadata(null)); + + #endregion + + #endregion + + #region Constructors + + /// + /// Static constructor + /// + [SuppressMessage("Microsoft.Performance", "CA1810")] + static RadioButton() + { + Type type = typeof(RadioButton); + DefaultStyleKeyProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(type)); + ContextMenuService.Attach(type); + ToolTipService.Attach(type); + StyleProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceStyle))); + } + + // Coerce object style + static object OnCoerceStyle(DependencyObject d, object basevalue) + { + if (basevalue == null) + { + //basevalue = (d as FrameworkElement).TryFindResource(typeof(QuickAccessToolBar)); + basevalue = (d as FrameworkElement).TryFindResource(typeof(RadioButton)); + } + + return basevalue; + } + + /// + /// Default constructor + /// + public RadioButton() + { + ContextMenuService.Coerce(this); + } + + #endregion + + #region Quick Access Item Creating + + /// + /// Gets control which represents shortcut item. + /// This item MUST be syncronized with the original + /// and send command to original one control. + /// + /// Control which represents shortcut item + public virtual FrameworkElement CreateQuickAccessItem() + { + RadioButton button = new RadioButton(); + + RibbonControl.Bind(this, button, "IsChecked", IsCheckedProperty, BindingMode.TwoWay); + button.Click += ((sender, e) => this.RaiseEvent(e)); + RibbonControl.BindQuickAccessItem(this, button); + + return button; + } + + /// + /// Gets or sets whether control can be added to quick access toolbar + /// + public bool CanAddToQuickAccessToolBar + { + get { return (bool)this.GetValue(CanAddToQuickAccessToolBarProperty); } + set { this.SetValue(CanAddToQuickAccessToolBarProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for CanAddToQuickAccessToolBar. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty CanAddToQuickAccessToolBarProperty = RibbonControl.CanAddToQuickAccessToolBarProperty.AddOwner(typeof(RadioButton), new UIPropertyMetadata(true, RibbonControl.OnCanAddToQuickAccessToolbarChanged)); + + #endregion + + #region Implementation of IKeyTipedControl + + /// + /// Handles key tip pressed + /// + public void OnKeyTipPressed() + { + this.OnClick(); + } + + /// + /// Handles back navigation with KeyTips + /// + public void OnKeyTipBack() + { + } + + #endregion + } } \ No newline at end of file diff --git a/Fluent/Controls/Ribbon.cs b/Fluent.Ribbon/Controls/Ribbon.cs similarity index 97% rename from Fluent/Controls/Ribbon.cs rename to Fluent.Ribbon/Controls/Ribbon.cs index 2a40e1ff1..6c18d88f8 100644 --- a/Fluent/Controls/Ribbon.cs +++ b/Fluent.Ribbon/Controls/Ribbon.cs @@ -1,2229 +1,2229 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Collections.Specialized; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.IO; -using System.IO.IsolatedStorage; -using System.Linq; -using System.Text; -using System.Threading; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Input; -using System.Windows.Markup; - -namespace Fluent -{ - using System.ComponentModel; - using System.Windows.Threading; - using Fluent.Extensions; - - // TODO: improve style parts naming & using - - /// - /// Represents the main Ribbon control which consists of multiple tabs, each of which - /// containing groups of controls. The Ribbon also provides improved context - /// menus, enhanced screen tips, and keyboard shortcuts. - /// - [ContentProperty("Tabs")] - [SuppressMessage("Microsoft.Maintainability", "CA1506")] - [SuppressMessage("Microsoft.Design", "CA1001")] - public class Ribbon : Control - { - private readonly RibbonState ribbonState; - - #region Localization - - // Localizable properties - static readonly RibbonLocalization localization = new RibbonLocalization(); - - /// - /// Gets localizable properties - /// - public static RibbonLocalization Localization - { - get { return localization; } - } - - #endregion - - #region Constants - - /// - /// Minimal width of ribbon parent window - /// - public const double MinimalVisibleWidth = 300; - /// - /// Minimal height of ribbon parent window - /// - public const double MinimalVisibleHeight = 250; - - #endregion - - #region ContextMenu - - private static readonly Dictionary contextMenus = new Dictionary(); - - /// - /// Context menu for ribbon in current thread - /// - public static System.Windows.Controls.ContextMenu RibbonContextMenu - { - get - { - if (!contextMenus.ContainsKey(Thread.CurrentThread.ManagedThreadId)) InitRibbonContextMenu(); - return contextMenus[Thread.CurrentThread.ManagedThreadId]; - } - } - - // Context menu owner ribbon - private static Ribbon contextMenuOwner; - - // Context menu items - private static Dictionary addToQuickAccessMenuItemDictionary = new Dictionary(); - private static System.Windows.Controls.MenuItem addToQuickAccessMenuItem - { - get { return addToQuickAccessMenuItemDictionary[Thread.CurrentThread.ManagedThreadId]; } - } - private static Dictionary addGroupToQuickAccessMenuItemDictionary = new Dictionary(); - private static System.Windows.Controls.MenuItem addGroupToQuickAccessMenuItem - { - get { return addGroupToQuickAccessMenuItemDictionary[Thread.CurrentThread.ManagedThreadId]; } - } - private static Dictionary addMenuToQuickAccessMenuItemDictionary = new Dictionary(); - private static System.Windows.Controls.MenuItem addMenuToQuickAccessMenuItem - { - get { return addMenuToQuickAccessMenuItemDictionary[Thread.CurrentThread.ManagedThreadId]; } - } - private static Dictionary addGalleryToQuickAccessMenuItemDictionary = new Dictionary(); - private static System.Windows.Controls.MenuItem addGalleryToQuickAccessMenuItem - { - get { return addGalleryToQuickAccessMenuItemDictionary[Thread.CurrentThread.ManagedThreadId]; } - } - private static Dictionary removeFromQuickAccessMenuItemDictionary = new Dictionary(); - private static System.Windows.Controls.MenuItem removeFromQuickAccessMenuItem - { - get { return removeFromQuickAccessMenuItemDictionary[Thread.CurrentThread.ManagedThreadId]; } - } - private static Dictionary showQuickAccessToolbarBelowTheRibbonMenuItemDictionary = new Dictionary(); - private static System.Windows.Controls.MenuItem showQuickAccessToolbarBelowTheRibbonMenuItem - { - get { return showQuickAccessToolbarBelowTheRibbonMenuItemDictionary[Thread.CurrentThread.ManagedThreadId]; } - } - private static Dictionary showQuickAccessToolbarAboveTheRibbonMenuItemDictionary = new Dictionary(); - private static System.Windows.Controls.MenuItem showQuickAccessToolbarAboveTheRibbonMenuItem - { - get { return showQuickAccessToolbarAboveTheRibbonMenuItemDictionary[Thread.CurrentThread.ManagedThreadId]; } - } - private static Dictionary minimizeTheRibbonMenuItemDictionary = new Dictionary(); - private static System.Windows.Controls.MenuItem minimizeTheRibbonMenuItem - { - get { return minimizeTheRibbonMenuItemDictionary[Thread.CurrentThread.ManagedThreadId]; } - } - private static Dictionary customizeQuickAccessToolbarMenuItemDictionary = new Dictionary(); - private static System.Windows.Controls.MenuItem customizeQuickAccessToolbarMenuItem - { - get { return customizeQuickAccessToolbarMenuItemDictionary[Thread.CurrentThread.ManagedThreadId]; } - } - private static Dictionary customizeTheRibbonMenuItemDictionary = new Dictionary(); - private static System.Windows.Controls.MenuItem customizeTheRibbonMenuItem - { - get { return customizeTheRibbonMenuItemDictionary[Thread.CurrentThread.ManagedThreadId]; } - } - private static Dictionary firstSeparatorDictionary = new Dictionary(); - private static Separator firstSeparator - { - get { return firstSeparatorDictionary[Thread.CurrentThread.ManagedThreadId]; } - } - private static Dictionary secondSeparatorDictionary = new Dictionary(); - private static Separator secondSeparator - { - get { return secondSeparatorDictionary[Thread.CurrentThread.ManagedThreadId]; } - } - - // Initialize ribbon context menu - private static void InitRibbonContextMenu() - { - contextMenus.Add(Thread.CurrentThread.ManagedThreadId, new System.Windows.Controls.ContextMenu()); - RibbonContextMenu.Opened += OnContextMenuOpened; - - // Add to quick access toolbar - addToQuickAccessMenuItemDictionary.Add(Thread.CurrentThread.ManagedThreadId, new System.Windows.Controls.MenuItem { Command = AddToQuickAccessCommand }); - RibbonContextMenu.Items.Add(addToQuickAccessMenuItem); - RibbonControl.Bind(Localization, addToQuickAccessMenuItem, "RibbonContextMenuAddItem", HeaderedItemsControl.HeaderProperty, BindingMode.OneWay); - RibbonControl.Bind(RibbonContextMenu, addToQuickAccessMenuItem, "PlacementTarget", System.Windows.Controls.MenuItem.CommandParameterProperty, BindingMode.OneWay); - - // Add group to quick access toolbar - addGroupToQuickAccessMenuItemDictionary.Add(Thread.CurrentThread.ManagedThreadId, new System.Windows.Controls.MenuItem { Command = AddToQuickAccessCommand }); - RibbonContextMenu.Items.Add(addGroupToQuickAccessMenuItem); - RibbonControl.Bind(Localization, addGroupToQuickAccessMenuItem, "RibbonContextMenuAddGroup", HeaderedItemsControl.HeaderProperty, BindingMode.OneWay); - RibbonControl.Bind(RibbonContextMenu, addGroupToQuickAccessMenuItem, "PlacementTarget", System.Windows.Controls.MenuItem.CommandParameterProperty, BindingMode.OneWay); - - // Add menu item to quick access toolbar - addMenuToQuickAccessMenuItemDictionary.Add(Thread.CurrentThread.ManagedThreadId, new System.Windows.Controls.MenuItem { Command = AddToQuickAccessCommand }); - RibbonContextMenu.Items.Add(addMenuToQuickAccessMenuItem); - RibbonControl.Bind(Localization, addMenuToQuickAccessMenuItem, "RibbonContextMenuAddMenu", HeaderedItemsControl.HeaderProperty, BindingMode.OneWay); - RibbonControl.Bind(RibbonContextMenu, addMenuToQuickAccessMenuItem, "PlacementTarget", System.Windows.Controls.MenuItem.CommandParameterProperty, BindingMode.OneWay); - - // Add gallery to quick access toolbar - addGalleryToQuickAccessMenuItemDictionary.Add(Thread.CurrentThread.ManagedThreadId, new System.Windows.Controls.MenuItem { Command = AddToQuickAccessCommand }); - RibbonContextMenu.Items.Add(addGalleryToQuickAccessMenuItem); - RibbonControl.Bind(Localization, addGalleryToQuickAccessMenuItem, "RibbonContextMenuAddGallery", HeaderedItemsControl.HeaderProperty, BindingMode.OneWay); - RibbonControl.Bind(RibbonContextMenu, addGalleryToQuickAccessMenuItem, "PlacementTarget", System.Windows.Controls.MenuItem.CommandParameterProperty, BindingMode.OneWay); - - // Remove from quick access toolbar - removeFromQuickAccessMenuItemDictionary.Add(Thread.CurrentThread.ManagedThreadId, new System.Windows.Controls.MenuItem { Command = RemoveFromQuickAccessCommand }); - RibbonContextMenu.Items.Add(removeFromQuickAccessMenuItem); - RibbonControl.Bind(Localization, removeFromQuickAccessMenuItem, "RibbonContextMenuRemoveItem", HeaderedItemsControl.HeaderProperty, BindingMode.OneWay); - RibbonControl.Bind(RibbonContextMenu, removeFromQuickAccessMenuItem, "PlacementTarget", System.Windows.Controls.MenuItem.CommandParameterProperty, BindingMode.OneWay); - - // Separator - firstSeparatorDictionary.Add(Thread.CurrentThread.ManagedThreadId, new Separator()); - RibbonContextMenu.Items.Add(firstSeparator); - - // Customize quick access toolbar - customizeQuickAccessToolbarMenuItemDictionary.Add(Thread.CurrentThread.ManagedThreadId, new System.Windows.Controls.MenuItem { Command = CustomizeQuickAccessToolbarCommand }); - RibbonContextMenu.Items.Add(customizeQuickAccessToolbarMenuItem); - RibbonControl.Bind(Localization, customizeQuickAccessToolbarMenuItem, "RibbonContextMenuCustomizeQuickAccessToolBar", HeaderedItemsControl.HeaderProperty, BindingMode.OneWay); - RibbonControl.Bind(RibbonContextMenu, customizeQuickAccessToolbarMenuItem, "PlacementTarget", System.Windows.Controls.MenuItem.CommandParameterProperty, BindingMode.OneWay); - - // Show quick access below the ribbon - showQuickAccessToolbarBelowTheRibbonMenuItemDictionary.Add(Thread.CurrentThread.ManagedThreadId, new System.Windows.Controls.MenuItem { Command = ShowQuickAccessBelowCommand }); - RibbonContextMenu.Items.Add(showQuickAccessToolbarBelowTheRibbonMenuItem); - RibbonControl.Bind(Localization, showQuickAccessToolbarBelowTheRibbonMenuItem, "RibbonContextMenuShowBelow", HeaderedItemsControl.HeaderProperty, BindingMode.OneWay); - RibbonControl.Bind(RibbonContextMenu, showQuickAccessToolbarBelowTheRibbonMenuItem, "PlacementTarget", System.Windows.Controls.MenuItem.CommandParameterProperty, BindingMode.OneWay); - - // Show quick access above the ribbon - showQuickAccessToolbarAboveTheRibbonMenuItemDictionary.Add(Thread.CurrentThread.ManagedThreadId, new System.Windows.Controls.MenuItem { Command = ShowQuickAccessAboveCommand }); - RibbonContextMenu.Items.Add(showQuickAccessToolbarAboveTheRibbonMenuItem); - RibbonControl.Bind(Localization, showQuickAccessToolbarAboveTheRibbonMenuItem, "RibbonContextMenuShowAbove", HeaderedItemsControl.HeaderProperty, BindingMode.OneWay); - RibbonControl.Bind(RibbonContextMenu, showQuickAccessToolbarAboveTheRibbonMenuItem, "PlacementTarget", System.Windows.Controls.MenuItem.CommandParameterProperty, BindingMode.OneWay); - - // Separator - secondSeparatorDictionary.Add(Thread.CurrentThread.ManagedThreadId, new Separator()); - RibbonContextMenu.Items.Add(secondSeparator); - - // Customize the ribbon - customizeTheRibbonMenuItemDictionary.Add(Thread.CurrentThread.ManagedThreadId, new System.Windows.Controls.MenuItem { Command = CustomizeTheRibbonCommand }); - RibbonContextMenu.Items.Add(customizeTheRibbonMenuItem); - RibbonControl.Bind(Localization, customizeTheRibbonMenuItem, "RibbonContextMenuCustomizeRibbon", HeaderedItemsControl.HeaderProperty, BindingMode.OneWay); - RibbonControl.Bind(RibbonContextMenu, customizeTheRibbonMenuItem, "PlacementTarget", System.Windows.Controls.MenuItem.CommandParameterProperty, BindingMode.OneWay); - - // Minimize the ribbon - minimizeTheRibbonMenuItemDictionary.Add(Thread.CurrentThread.ManagedThreadId, new System.Windows.Controls.MenuItem { Command = ToggleMinimizeTheRibbonCommand }); - RibbonContextMenu.Items.Add(minimizeTheRibbonMenuItem); - RibbonControl.Bind(Localization, minimizeTheRibbonMenuItem, "RibbonContextMenuMinimizeRibbon", HeaderedItemsControl.HeaderProperty, BindingMode.OneWay); - RibbonControl.Bind(RibbonContextMenu, minimizeTheRibbonMenuItem, "PlacementTarget", System.Windows.Controls.MenuItem.CommandParameterProperty, BindingMode.OneWay); - } - - /// - /// Invoked whenever an unhandled routed event reaches this class in its route. Implement this method to add class handling for this event. - /// - /// The that contains the event data. - protected override void OnContextMenuOpening(ContextMenuEventArgs e) - { - contextMenuOwner = this; - base.OnContextMenuOpening(e); - } - - /// - /// Invoked whenever an unhandled routed event reaches this class in its route. Implement this method to add class handling for this event. - /// - /// Provides data about the event. - protected override void OnContextMenuClosing(ContextMenuEventArgs e) - { - contextMenuOwner = null; - base.OnContextMenuClosing(e); - } - - // Occurs when context menu is opening - private static void OnContextMenuOpened(object sender, RoutedEventArgs e) - { - var ribbon = contextMenuOwner; - - if (RibbonContextMenu == null - || ribbon == null) - { - return; - } - - addToQuickAccessMenuItem.CommandTarget = ribbon; - addGroupToQuickAccessMenuItem.CommandTarget = ribbon; - addMenuToQuickAccessMenuItem.CommandTarget = ribbon; - addGalleryToQuickAccessMenuItem.CommandTarget = ribbon; - removeFromQuickAccessMenuItem.CommandTarget = ribbon; - customizeQuickAccessToolbarMenuItem.CommandTarget = ribbon; - customizeTheRibbonMenuItem.CommandTarget = ribbon; - minimizeTheRibbonMenuItem.CommandTarget = ribbon; - showQuickAccessToolbarBelowTheRibbonMenuItem.CommandTarget = ribbon; - showQuickAccessToolbarAboveTheRibbonMenuItem.CommandTarget = ribbon; - - // Hide items for ribbon controls - addToQuickAccessMenuItem.Visibility = Visibility.Collapsed; - addGroupToQuickAccessMenuItem.Visibility = Visibility.Collapsed; - addMenuToQuickAccessMenuItem.Visibility = Visibility.Collapsed; - addGalleryToQuickAccessMenuItem.Visibility = Visibility.Collapsed; - removeFromQuickAccessMenuItem.Visibility = Visibility.Collapsed; - firstSeparator.Visibility = Visibility.Collapsed; - - // Hide customize quick access menu item - customizeQuickAccessToolbarMenuItem.Visibility = Visibility.Collapsed; - secondSeparator.Visibility = Visibility.Collapsed; - - // Set minimize the ribbon menu item state - minimizeTheRibbonMenuItem.IsChecked = ribbon.IsMinimized; - - // Set customize the ribbon menu item visibility - if (ribbon.CanCustomizeRibbon) - { - customizeTheRibbonMenuItem.Visibility = Visibility.Visible; - } - else - { - customizeTheRibbonMenuItem.Visibility = Visibility.Collapsed; - } - - // Hide quick access position menu items - showQuickAccessToolbarBelowTheRibbonMenuItem.Visibility = Visibility.Collapsed; - showQuickAccessToolbarAboveTheRibbonMenuItem.Visibility = Visibility.Collapsed; - - // If quick access toolbar is visible show - if (ribbon.IsQuickAccessToolBarVisible) - { - // Set quick access position menu items visibility - if (ribbon.CanQuickAccessLocationChanging) - { - if (ribbon.ShowQuickAccessToolBarAboveRibbon) - { - showQuickAccessToolbarBelowTheRibbonMenuItem.Visibility = Visibility.Visible; - } - else - { - showQuickAccessToolbarAboveTheRibbonMenuItem.Visibility = Visibility.Visible; - } - } - - if (ribbon.CanCustomizeQuickAccessToolBar) - { - customizeQuickAccessToolbarMenuItem.Visibility = Visibility.Visible; - } - - if (ribbon.CanQuickAccessLocationChanging - || ribbon.CanCustomizeQuickAccessToolBar) - { - secondSeparator.Visibility = Visibility.Visible; - } - else - { - secondSeparator.Visibility = Visibility.Collapsed; - } - - if (ribbon.CanCustomizeQuickAccessToolBarItems) - { - // Gets control that raise menu opened - var control = RibbonContextMenu.PlacementTarget; - AddToQuickAccessCommand.CanExecute(null, control); - RemoveFromQuickAccessCommand.CanExecute(null, control); - - //Debug.WriteLine("Menu opened on "+control); - if (control != null) - { - firstSeparator.Visibility = Visibility.Visible; - - // Check for value because remove is only possible in the context menu of items in QA which represent the value for QA-items - if (ribbon.quickAccessElements.ContainsValue(control)) - { - // Control is on quick access - removeFromQuickAccessMenuItem.Visibility = Visibility.Visible; - } - else if (control is System.Windows.Controls.MenuItem) - { - // Control is menu item - addMenuToQuickAccessMenuItem.Visibility = Visibility.Visible; - } - else if ((control is Gallery) || - (control is InRibbonGallery)) - { - // Control is gallery - addGalleryToQuickAccessMenuItem.Visibility = Visibility.Visible; - } - else if (control is RibbonGroupBox) - { - // Control is group box - addGroupToQuickAccessMenuItem.Visibility = Visibility.Visible; - } - else if (control is IQuickAccessItemProvider) - { - // Its other control - addToQuickAccessMenuItem.Visibility = Visibility.Visible; - } - else - { - firstSeparator.Visibility = Visibility.Collapsed; - } - } - } - } - } - - #endregion - - #region Events - - /// - /// Occurs when selected tab has been changed (be aware that SelectedTab can be null) - /// - public event SelectionChangedEventHandler SelectedTabChanged; - - /// - /// Occurs when customize the ribbon - /// - public event EventHandler CustomizeTheRibbon; - - /// - /// Occurs when customize quick access toolbar - /// - public event EventHandler CustomizeQuickAccessToolbar; - - /// - /// Occurs when IsMinimized property is changing - /// - public event DependencyPropertyChangedEventHandler IsMinimizedChanged; - - /// - /// Occurs when IsCollapsed property is changing - /// - public event DependencyPropertyChangedEventHandler IsCollapsedChanged; - - #endregion - - #region Fields - - // Collection of contextual tab groups - private ObservableCollection contextualGroups; - - // Collection of tabs - private ObservableCollection tabs; - - // Collection of toolbar items - private ObservableCollection toolBarItems; - - // Ribbon quick access toolbar - private QuickAccessToolBar quickAccessToolBar; - - // Ribbon layout root - private Panel layoutRoot; - - // Handles F10, Alt and so on - readonly KeyTipService keyTipService; - - // Collection of quickaccess menu items - private ObservableCollection quickAccessItems; - - // Currently added in QAT items - private readonly Dictionary quickAccessElements = new Dictionary(); - - private Window ownerWindow; - - #endregion - - #region Properties - - #region Menu - - /// - /// Gets or sets file menu control (can be application menu button, backstage button and so on) - /// - public UIElement Menu - { - get { return (UIElement)this.GetValue(MenuProperty); } - set { this.SetValue(MenuProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Button. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty MenuProperty = - DependencyProperty.Register("Menu", typeof(UIElement), typeof(Ribbon), new UIPropertyMetadata(null, OnApplicationMenuChanged)); - - static void OnApplicationMenuChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - Ribbon ribbon = (Ribbon)d; - if (e.OldValue != null) ribbon.RemoveLogicalChild(e.OldValue); - if (e.NewValue != null) ribbon.AddLogicalChild(e.NewValue); - } - - #endregion - - /// - /// Window title - /// - public string Title - { - get { return (string)this.GetValue(TitleProperty); } - set { this.SetValue(TitleProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Title. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty TitleProperty = - DependencyProperty.Register("Title", typeof(string), typeof(Ribbon), new UIPropertyMetadata("", OnTitleChanged)); - - private static void OnTitleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var ribbon = d as Ribbon; - - if (ribbon != null - && ribbon.TitleBar != null) - { - ribbon.TitleBar.InvalidateMeasure(); - } - } - - /// - /// Gets or sets selected tab item - /// - public RibbonTabItem SelectedTabItem - { - get { return (RibbonTabItem)this.GetValue(SelectedTabItemProperty); } - set { this.SetValue(SelectedTabItemProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for SelectedTabItem. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty SelectedTabItemProperty = - DependencyProperty.Register("SelectedTabItem", typeof(RibbonTabItem), typeof(Ribbon), new UIPropertyMetadata(null, OnSelectedTabItemChanged)); - - private static void OnSelectedTabItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var ribbon = (Ribbon)d; - if (ribbon.TabControl != null) - { - ribbon.TabControl.SelectedItem = e.NewValue; - } - - var selectedItem = e.NewValue as RibbonTabItem; - - if (selectedItem != null - && ribbon.Tabs.Contains(selectedItem)) - { - ribbon.SelectedTabIndex = ribbon.Tabs.IndexOf(selectedItem); - } - else - { - ribbon.SelectedTabIndex = -1; - } - } - - /// - /// Gets or sets selected tab index - /// - public int SelectedTabIndex - { - get { return (int)this.GetValue(SelectedTabIndexProperty); } - set { this.SetValue(SelectedTabIndexProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for SelectedTabindex. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty SelectedTabIndexProperty = - DependencyProperty.Register("SelectedTabIndex", typeof(int), typeof(Ribbon), new UIPropertyMetadata(-1, OnSelectedTabIndexChanged)); - - private static void OnSelectedTabIndexChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var ribbon = (Ribbon)d; - var selectedIndex = (int)e.NewValue; - - if (ribbon.TabControl != null) - { - ribbon.TabControl.SelectedIndex = selectedIndex; - } - - if (selectedIndex >= 0 - && selectedIndex < ribbon.Tabs.Count) - { - ribbon.SelectedTabItem = ribbon.Tabs[selectedIndex]; - } - else - { - ribbon.SelectedTabItem = null; - } - } - - /// - /// Gets the first visible TabItem - /// - public RibbonTabItem FirstVisibleItem - { - get - { - return this.GetFirstVisibleItem(); - } - } - - /// - /// Gets the last visible TabItem - /// - public RibbonTabItem LastVisibleItem - { - get - { - return this.GetLastVisibleItem(); - } - } - - /// - /// Gets the list of currently active quick access elements. - /// - protected Dictionary QuickAccessElements - { - get - { - return this.quickAccessElements; - } - } - - /// - /// Gets ribbon titlebar - /// - internal RibbonTitleBar TitleBar { get; private set; } - - /// - /// Gets the Ribbon tab control - /// - internal RibbonTabControl TabControl { get; private set; } - - /// - /// Gets or sets whether quick access toolbar showes above ribbon - /// - public bool ShowQuickAccessToolBarAboveRibbon - { - get { return (bool)this.GetValue(ShowQuickAccessToolBarAboveRibbonProperty); } - set { this.SetValue(ShowQuickAccessToolBarAboveRibbonProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for ShowAboveRibbon. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty ShowQuickAccessToolBarAboveRibbonProperty = - DependencyProperty.Register("ShowQuickAccessToolBarAboveRibbon", typeof(bool), typeof(Ribbon), new UIPropertyMetadata(true, OnShowQuickAccesToolBarAboveRibbonChanged)); - - /// - /// Handles ShowQuickAccessToolBarAboveRibbon property changed - /// - /// Object - /// The event data - private static void OnShowQuickAccesToolBarAboveRibbonChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var ribbon = (Ribbon)d; - if (ribbon.TitleBar != null) - { - ribbon.TitleBar.InvalidateMeasure(); - } - - ribbon.ribbonState.SaveStateToMemoryStream(); - } - - /// - /// Gets collection of contextual tab groups - /// - public ObservableCollection ContextualGroups - { - get - { - if (this.contextualGroups == null) - { - this.contextualGroups = new ObservableCollection(); - this.contextualGroups.CollectionChanged += this.OnContextualGroupsCollectionChanged; - } - - return this.contextualGroups; - } - } - - /// - /// Handles collection of contextual tab groups ghanges - /// - /// Sender - /// The event data - private void OnContextualGroupsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) - { - switch (e.Action) - { - case NotifyCollectionChangedAction.Add: - for (int i = 0; i < e.NewItems.Count; i++) - { - if (this.TitleBar != null) this.TitleBar.Items.Insert(e.NewStartingIndex + i, e.NewItems[i]); - } - break; - - case NotifyCollectionChangedAction.Remove: - foreach (object item in e.OldItems) - { - if (this.TitleBar != null) this.TitleBar.Items.Remove(item); - } - break; - - case NotifyCollectionChangedAction.Replace: - foreach (object item in e.OldItems) - { - if (this.TitleBar != null) this.TitleBar.Items.Remove(item); - } - foreach (object item in e.NewItems) - { - if (this.TitleBar != null) this.TitleBar.Items.Add(item); - } - break; - case NotifyCollectionChangedAction.Reset: - if (this.TitleBar != null) this.TitleBar.Items.Clear(); - break; - } - - } - - /// - /// gets collection of ribbon tabs - /// - public ObservableCollection Tabs - { - get - { - if (this.tabs == null) - { - this.tabs = new ObservableCollection(); - this.tabs.CollectionChanged += this.OnTabsCollectionChanged; - } - - return this.tabs; - } - } - - /// - /// Handles collection of ribbon tabs changed - /// - /// Sender - /// The event data - private void OnTabsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) - { - if (this.TabControl == null) - { - return; - } - - switch (e.Action) - { - case NotifyCollectionChangedAction.Add: - for (var i = 0; i < e.NewItems.Count; i++) - { - this.TabControl.Items.Insert(e.NewStartingIndex + i, e.NewItems[i]); - } - break; - - case NotifyCollectionChangedAction.Remove: - foreach (var item in e.OldItems) - { - this.TabControl.Items.Remove(item); - } - break; - - case NotifyCollectionChangedAction.Replace: - foreach (var item in e.OldItems) - { - this.TabControl.Items.Remove(item); - } - - foreach (var item in e.NewItems) - { - this.TabControl.Items.Add(item); - } - break; - - case NotifyCollectionChangedAction.Reset: - this.TabControl.Items.Clear(); - break; - } - } - - /// - /// Gets collection of toolbar items - /// - public ObservableCollection ToolBarItems - { - get - { - if (this.toolBarItems == null) - { - this.toolBarItems = new ObservableCollection(); - this.toolBarItems.CollectionChanged += this.OnToolbarItemsCollectionChanged; - } - - return this.toolBarItems; - } - } - - /// - /// Handles collection of toolbar items changes - /// - /// Sender - /// The event data - private void OnToolbarItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) - { - switch (e.Action) - { - case NotifyCollectionChangedAction.Add: - for (int i = 0; i < e.NewItems.Count; i++) - { - if (this.TabControl != null) - this.TabControl.ToolBarItems.Insert(e.NewStartingIndex + i, (UIElement)e.NewItems[i]); - } - break; - - case NotifyCollectionChangedAction.Remove: - foreach (object item in e.OldItems) - { - if (this.TabControl != null) - this.TabControl.ToolBarItems.Remove(item as UIElement); - } - break; - - case NotifyCollectionChangedAction.Replace: - foreach (object item in e.OldItems) - { - if (this.TabControl != null) - this.TabControl.ToolBarItems.Remove(item as UIElement); - } - foreach (object item in e.NewItems) - { - if (this.TabControl != null) - this.TabControl.ToolBarItems.Add(item as UIElement); - } - break; - } - - } - - /// - /// Gets quick access toolbar associated with the ribbon - /// - internal QuickAccessToolBar QuickAccessToolBar - { - get { return this.quickAccessToolBar; } - } - - /// - /// Gets an enumerator for logical child elements of this element. - /// - protected override IEnumerator LogicalChildren - { - get - { - if (this.layoutRoot != null) - { - yield return this.layoutRoot; - } - - if (this.Menu != null) - { - yield return this.Menu; - } - - if (this.quickAccessToolBar != null) - { - yield return this.quickAccessToolBar; - } - - if (this.TabControl != null - && this.TabControl.ToolbarPanel != null) - { - yield return this.TabControl.ToolbarPanel; - } - } - } - - /// - /// Gets collection of quick access menu items - /// - public ObservableCollection QuickAccessItems - { - get - { - if (this.quickAccessItems == null) - { - this.quickAccessItems = new ObservableCollection(); - this.quickAccessItems.CollectionChanged += this.OnQuickAccessItemsCollectionChanged; - } - - return this.quickAccessItems; - } - } - - /// - /// Handles collection of quick access menu items changes - /// - /// Sender - /// The event data - private void OnQuickAccessItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) - { - switch (e.Action) - { - case NotifyCollectionChangedAction.Add: - for (var i = 0; i < e.NewItems.Count; i++) - { - var menuItem = (QuickAccessMenuItem)e.NewItems[i]; - if (this.quickAccessToolBar != null) - { - this.quickAccessToolBar.QuickAccessItems.Insert(e.NewStartingIndex + i, menuItem); - } - menuItem.Ribbon = this; - } - break; - - case NotifyCollectionChangedAction.Remove: - foreach (var item in e.OldItems.OfType()) - { - var menuItem = item; - if (this.quickAccessToolBar != null) - { - this.quickAccessToolBar.QuickAccessItems.Remove(menuItem); - } - menuItem.Ribbon = null; - } - break; - - case NotifyCollectionChangedAction.Replace: - foreach (var item in e.OldItems.OfType()) - { - var menuItem = item; - if (this.quickAccessToolBar != null) - { - this.quickAccessToolBar.QuickAccessItems.Remove(menuItem); - } - menuItem.Ribbon = null; - } - foreach (var item in e.NewItems.OfType()) - { - var menuItem = item; - if (this.quickAccessToolBar != null) - { - this.quickAccessToolBar.QuickAccessItems.Add(menuItem); - } - menuItem.Ribbon = this; - } - break; - } - } - - /// - /// Gets or sets whether Customize Quick Access Toolbar menu item is shown - /// - public bool CanCustomizeQuickAccessToolBar - { - get { return (bool)this.GetValue(CanCustomizeQuickAccessToolBarProperty); } - set { this.SetValue(CanCustomizeQuickAccessToolBarProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for CanCustomizeQuickAccessToolBar. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty CanCustomizeQuickAccessToolBarProperty = - DependencyProperty.Register("CanCustomizeQuickAccessToolBar", typeof(bool), - typeof(Ribbon), new UIPropertyMetadata(false)); - - /// - /// Gets or sets whether items can be added or removed from the quick access toolbar by users. - /// - public bool CanCustomizeQuickAccessToolBarItems - { - get { return (bool)this.GetValue(CanCustomizeQuickAccessToolBarItemsProperty); } - set { this.SetValue(CanCustomizeQuickAccessToolBarItemsProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for CanCustomizeQuickAccessToolBarItems. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty CanCustomizeQuickAccessToolBarItemsProperty = - DependencyProperty.Register("CanCustomizeQuickAccessToolBarItems", typeof(bool), typeof(Ribbon), new PropertyMetadata(true)); - - /// - /// Gets or sets whether Customize Ribbon menu item is shown - /// - public bool CanCustomizeRibbon - { - get { return (bool)this.GetValue(CanCustomizeRibbonProperty); } - set { this.SetValue(CanCustomizeRibbonProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for CanCustomizeRibbon. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty CanCustomizeRibbonProperty = - DependencyProperty.Register("CanCustomizeRibbon", typeof(bool), - typeof(Ribbon), new UIPropertyMetadata(false)); - - - /// - /// Gets or sets whether ribbon is minimized - /// - public bool IsMinimized - { - get { return (bool)this.GetValue(IsMinimizedProperty); } - set { this.SetValue(IsMinimizedProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for IsMinimized. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IsMinimizedProperty = - DependencyProperty.Register("IsMinimized", typeof(bool), - typeof(Ribbon), new UIPropertyMetadata(false, OnIsMinimizedChanged)); - - private static void OnIsMinimizedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var ribbon = (Ribbon)d; - - if (ribbon.IsMinimizedChanged != null) - { - ribbon.IsMinimizedChanged(ribbon, e); - } - } - - /// - /// Gets or sets the height of the gap between the ribbon and the content - /// - public double ContentGapHeight - { - get { return (double)this.GetValue(ContentGapHeightProperty); } - set { this.SetValue(ContentGapHeightProperty, value); } - } - - /// - /// DependencyProperty for - /// - public static readonly DependencyProperty ContentGapHeightProperty = - DependencyProperty.Register("ContentGapHeight", typeof(double), typeof(Ribbon), new UIPropertyMetadata(5D)); - - // todo check if IsCollapsed and IsAutomaticCollapseEnabled should be reduced to one shared property for RibbonWindow and Ribbon - /// - /// Gets whether ribbon is collapsed - /// - public bool IsCollapsed - { - get { return (bool)this.GetValue(IsCollapsedProperty); } - set { this.SetValue(IsCollapsedProperty, value); } - } - - /// - /// DependencyProperty for - /// - public static readonly DependencyProperty IsCollapsedProperty = - DependencyProperty.Register("IsCollapsed", typeof(bool), - typeof(Ribbon), new FrameworkPropertyMetadata(false, OnIsCollapsedChanged)); - - private static void OnIsCollapsedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var ribbon = (Ribbon)d; - if (ribbon.IsCollapsedChanged != null) - { - ribbon.IsCollapsedChanged(ribbon, e); - } - } - - /// - /// Defines if the Ribbon should automatically set when the width or height of the owner window drop under or - /// - public bool IsAutomaticCollapseEnabled - { - get { return (bool)this.GetValue(IsAutomaticCollapseEnabledProperty); } - set { this.SetValue(IsAutomaticCollapseEnabledProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for IsCollapsed. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IsAutomaticCollapseEnabledProperty = - DependencyProperty.Register("IsAutomaticCollapseEnabled", typeof(bool), typeof(Ribbon), new PropertyMetadata(true)); - - /// - /// Gets or sets whether QAT is visible - /// - public bool IsQuickAccessToolBarVisible - { - get { return (bool)this.GetValue(IsQuickAccessToolBarVisibleProperty); } - set { this.SetValue(IsQuickAccessToolBarVisibleProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for IsQuickAccessToolBarVisible. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IsQuickAccessToolBarVisibleProperty = - DependencyProperty.Register("IsQuickAccessToolBarVisible", typeof(bool), typeof(Ribbon), new UIPropertyMetadata(true)); - - - /// - /// Gets or sets whether user can change location of QAT - /// - public bool CanQuickAccessLocationChanging - { - get { return (bool)this.GetValue(CanQuickAccessLocationChangingProperty); } - set { this.SetValue(CanQuickAccessLocationChangingProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for CanQuickAccessLocationChanging. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty CanQuickAccessLocationChangingProperty = - DependencyProperty.Register("CanQuickAccessLocationChanging", typeof(bool), typeof(Ribbon), new UIPropertyMetadata(true)); - - - /// - /// Checks if any keytips are visible. - /// - public bool AreAnyKeyTipsVisible - { - get - { - if (this.keyTipService != null) - { - return this.keyTipService.AreAnyKeyTipsVisible; - } - - return false; - } - } - #endregion - - #region Commands - - /// - /// Gets add to quick access toolbar command - /// - public static RoutedCommand AddToQuickAccessCommand = new RoutedCommand("AddToQuickAccessCommand", typeof(Ribbon)); - - /// - /// Gets remove from quick access command - /// - public static RoutedCommand RemoveFromQuickAccessCommand = new RoutedCommand("RemoveFromQuickAccessCommand", typeof(Ribbon)); - - /// - /// Gets show quick access above command - /// - public static RoutedCommand ShowQuickAccessAboveCommand = new RoutedCommand("ShowQuickAccessAboveCommand", typeof(Ribbon)); - - /// - /// Gets show quick access below command - /// - public static RoutedCommand ShowQuickAccessBelowCommand = new RoutedCommand("ShowQuickAccessBelowCommand", typeof(Ribbon)); - - /// - /// Gets toggle ribbon minimize command - /// - public static RoutedCommand ToggleMinimizeTheRibbonCommand = new RoutedCommand("ToggleMinimizeTheRibbonCommand", typeof(Ribbon)); - - /// - /// Gets customize quick access toolbar command - /// - public static RoutedCommand CustomizeQuickAccessToolbarCommand = new RoutedCommand("CustomizeQuickAccessToolbarCommand", typeof(Ribbon)); - - /// - /// Gets customize the ribbon command - /// - public static RoutedCommand CustomizeTheRibbonCommand = new RoutedCommand("CustomizeTheRibbonCommand", typeof(Ribbon)); - - // Occurs when customize toggle minimize command can execute handles - private static void OnToggleMinimizeTheRibbonCommandCanExecute(object sender, CanExecuteRoutedEventArgs e) - { - var ribbon = sender as Ribbon; - - if (ribbon != null - && ribbon.TabControl != null) - { - e.CanExecute = true; - } - } - - // Occurs when toggle minimize command executed - private static void OnToggleMinimizeTheRibbonCommandExecuted(object sender, ExecutedRoutedEventArgs e) - { - var ribbon = sender as Ribbon; - if (ribbon != null - && ribbon.TabControl != null) - { - ribbon.TabControl.IsMinimized = !ribbon.TabControl.IsMinimized; - } - } - - // Occurs when show quick access below command executed - private static void OnShowQuickAccessBelowCommandExecuted(object sender, ExecutedRoutedEventArgs e) - { - var ribbon = sender as Ribbon; - - if (ribbon == null) - { - return; - } - - ribbon.ShowQuickAccessToolBarAboveRibbon = false; - } - - // Occurs when show quick access above command executed - private static void OnShowQuickAccessAboveCommandExecuted(object sender, ExecutedRoutedEventArgs e) - { - var ribbon = sender as Ribbon; - - if (ribbon == null) - { - return; - } - ribbon.ShowQuickAccessToolBarAboveRibbon = true; - } - - // Occurs when remove from quick access command executed - private static void OnRemoveFromQuickAccessCommandExecuted(object sender, ExecutedRoutedEventArgs e) - { - var ribbon = sender as Ribbon; - - if (ribbon == null) - { - return; - } - - if (ribbon.quickAccessToolBar != null) - { - var element = ribbon.quickAccessElements.First(x => x.Value == e.Parameter).Key; - ribbon.RemoveFromQuickAccessToolBar(element); - } - } - - // Occurs when add to quick access command executed - private static void OnAddToQuickAccessCommandExecuted(object sender, ExecutedRoutedEventArgs e) - { - var ribbon = sender as Ribbon; - - if (ribbon == null) - { - return; - } - - if (ribbon.quickAccessToolBar != null) - { - ribbon.AddToQuickAccessToolBar(e.Parameter as UIElement); - } - } - - // Occurs when customize quick access command executed - private static void OnCustomizeQuickAccessToolbarCommandExecuted(object sender, ExecutedRoutedEventArgs e) - { - var ribbon = sender as Ribbon; - - if (ribbon == null) - { - return; - } - - if (ribbon.CustomizeQuickAccessToolbar != null) - { - ribbon.CustomizeQuickAccessToolbar(sender, EventArgs.Empty); - } - } - - // Occurs when customize the ribbon command executed - private static void OnCustomizeTheRibbonCommandExecuted(object sender, ExecutedRoutedEventArgs e) - { - var ribbon = sender as Ribbon; - - if (ribbon == null) - { - return; - } - - if (ribbon.CustomizeTheRibbon != null) - { - ribbon.CustomizeTheRibbon(sender, EventArgs.Empty); - } - } - - // Occurs when customize quick access command can execute handles - private static void OnCustomizeQuickAccessToolbarCommandCanExecute(object sender, CanExecuteRoutedEventArgs e) - { - var ribbon = sender as Ribbon; - - if (ribbon == null) - { - return; - } - - e.CanExecute = ribbon.CanCustomizeQuickAccessToolBar; - } - - // Occurs when customize the ribbon command can execute handles - private static void OnCustomizeTheRibbonCommandCanExecute(object sender, CanExecuteRoutedEventArgs e) - { - var ribbon = sender as Ribbon; - - if (ribbon == null) - { - return; - } - - e.CanExecute = ribbon.CanCustomizeRibbon; - } - - // Occurs when remove from quick access command can execute handles - private static void OnRemoveFromQuickAccessCommandCanExecute(object sender, CanExecuteRoutedEventArgs e) - { - var ribbon = sender as Ribbon; - - if (ribbon == null) - { - return; - } - - if (ribbon.IsQuickAccessToolBarVisible) - { - e.CanExecute = ribbon.quickAccessElements.ContainsValue(e.Parameter as UIElement); - } - else - { - e.CanExecute = false; - } - } - - // Occurs when add to quick access command can execute handles - private static void OnAddToQuickAccessCommandCanExecute(object sender, CanExecuteRoutedEventArgs e) - { - var ribbon = sender as Ribbon; - - if (ribbon != null - && ribbon.IsQuickAccessToolBarVisible - && QuickAccessItemsProvider.IsSupported(e.Parameter as UIElement) - && ribbon.IsInQuickAccessToolBar(e.Parameter as UIElement) == false) - { - if (e.Parameter is Gallery) - { - e.CanExecute = ribbon.IsInQuickAccessToolBar(FindParentRibbonControl(e.Parameter as DependencyObject) as UIElement) == false; - } - else - { - e.CanExecute = ribbon.IsInQuickAccessToolBar(e.Parameter as UIElement) == false; - } - } - else - { - e.CanExecute = false; - } - } - - #endregion - - #region Constructors - - /// - /// Static constructor - /// - [SuppressMessage("Microsoft.Performance", "CA1810")] - static Ribbon() - { - DefaultStyleKeyProperty.OverrideMetadata(typeof(Ribbon), new FrameworkPropertyMetadata(typeof(Ribbon))); - - // Subscribe to menu commands - CommandManager.RegisterClassCommandBinding(typeof(Ribbon), new CommandBinding(AddToQuickAccessCommand, OnAddToQuickAccessCommandExecuted, OnAddToQuickAccessCommandCanExecute)); - CommandManager.RegisterClassCommandBinding(typeof(Ribbon), new CommandBinding(RemoveFromQuickAccessCommand, OnRemoveFromQuickAccessCommandExecuted, OnRemoveFromQuickAccessCommandCanExecute)); - CommandManager.RegisterClassCommandBinding(typeof(Ribbon), new CommandBinding(ShowQuickAccessAboveCommand, OnShowQuickAccessAboveCommandExecuted)); - CommandManager.RegisterClassCommandBinding(typeof(Ribbon), new CommandBinding(ShowQuickAccessBelowCommand, OnShowQuickAccessBelowCommandExecuted)); - CommandManager.RegisterClassCommandBinding(typeof(Ribbon), new CommandBinding(ToggleMinimizeTheRibbonCommand, OnToggleMinimizeTheRibbonCommandExecuted, OnToggleMinimizeTheRibbonCommandCanExecute)); - CommandManager.RegisterClassCommandBinding(typeof(Ribbon), new CommandBinding(CustomizeTheRibbonCommand, OnCustomizeTheRibbonCommandExecuted, OnCustomizeTheRibbonCommandCanExecute)); - CommandManager.RegisterClassCommandBinding(typeof(Ribbon), new CommandBinding(CustomizeQuickAccessToolbarCommand, OnCustomizeQuickAccessToolbarCommandExecuted, OnCustomizeQuickAccessToolbarCommandCanExecute)); - - InitRibbonContextMenu(); - } - - /// - /// Default constructor - /// - public Ribbon() - { - this.VerticalAlignment = VerticalAlignment.Top; - KeyboardNavigation.SetDirectionalNavigation(this, KeyboardNavigationMode.Contained); - - this.keyTipService = new KeyTipService(this); - - this.Loaded += this.OnLoaded; - this.Unloaded += this.OnUnloaded; - - this.ribbonState = new RibbonState(this); - } - - #endregion - - #region Overrides - - private void OnSizeChanged(object sender, SizeChangedEventArgs e) - { - this.MaintainIsCollapsed(); - } - - private void MaintainIsCollapsed() - { - if (this.IsAutomaticCollapseEnabled == false - || this.ownerWindow == null) - { - return; - } - - if (this.ownerWindow.ActualWidth < MinimalVisibleWidth - || this.ownerWindow.ActualHeight < MinimalVisibleHeight) - { - this.IsCollapsed = true; - } - else - { - this.IsCollapsed = false; - } - } - - /// - /// Invoked whenever an unhandled System.Windows.UIElement.GotFocus - /// event reaches this element in its route. - /// - /// The System.Windows.RoutedEventArgs that contains the event data. - protected override void OnGotFocus(RoutedEventArgs e) - { - if (this.TabControl == null) - { - return; - } - - var ribbonTabItem = (RibbonTabItem)this.TabControl.SelectedItem; - if (ribbonTabItem != null) - { - ribbonTabItem.Focus(); - } - } - - /// - /// When overridden in a derived class, is invoked whenever application code or - /// internal processes call System.Windows.FrameworkElement.ApplyTemplate(). - /// - [SuppressMessage("Microsoft.Maintainability", "CA1502")] - public override void OnApplyTemplate() - { - if (this.layoutRoot != null) - { - this.RemoveLogicalChild(this.layoutRoot); - } - - this.layoutRoot = this.GetTemplateChild("PART_LayoutRoot") as Panel; - - if (this.layoutRoot != null) - { - this.AddLogicalChild(this.layoutRoot); - } - - if (this.TitleBar != null) - { - foreach (var ribbonContextualTabGroup in this.ContextualGroups) - { - this.TitleBar.Items.Remove(ribbonContextualTabGroup); - } - - // Make sure everything is cleared - this.TitleBar.Items.Clear(); - } - - this.TitleBar = this.GetTemplateChild("PART_RibbonTitleBar") as RibbonTitleBar; - - if (this.TitleBar != null) - { - foreach (var contextualTabGroup in this.ContextualGroups) - { - this.TitleBar.Items.Add(contextualTabGroup); - } - } - - var selectedTab = this.SelectedTabItem; - if (this.TabControl != null) - { - this.TabControl.SelectionChanged -= this.OnTabControlSelectionChanged; - selectedTab = this.TabControl.SelectedItem as RibbonTabItem; - - foreach (var ribbonTabItem in this.Tabs) - { - this.TabControl.Items.Remove(ribbonTabItem); - } - - // Make sure everything is cleared - this.TabControl.Items.Clear(); - - foreach (var toolBarItem in this.ToolBarItems) - { - this.TabControl.ToolBarItems.Remove(toolBarItem); - } - - // Make sure everything is cleared - this.TabControl.ToolBarItems.Clear(); - } - - this.TabControl = this.GetTemplateChild("PART_RibbonTabControl") as RibbonTabControl; - - if (this.TabControl != null) - { - this.TabControl.SelectionChanged += this.OnTabControlSelectionChanged; - - this.TabControl.IsMinimized = this.IsMinimized; - this.TabControl.ContentGapHeight = this.ContentGapHeight; - - this.TabControl.SetBinding(RibbonTabControl.IsMinimizedProperty, new Binding("IsMinimized") { Source = this, Mode = BindingMode.TwoWay }); - this.TabControl.SetBinding(RibbonTabControl.ContentGapHeightProperty, new Binding("ContentGapHeight") { Source = this, Mode = BindingMode.OneWay }); - - foreach (var ribbonTabItem in this.Tabs) - { - this.TabControl.Items.Add(ribbonTabItem); - } - - this.TabControl.SelectedItem = selectedTab; - - foreach (var toolBarItem in this.ToolBarItems) - { - this.TabControl.ToolBarItems.Add(toolBarItem); - } - } - - if (this.quickAccessToolBar != null) - { - if (this.AutomaticStateManagement == false - || this.ribbonState.IsStateLoaded) - { - this.ribbonState.SaveStateToMemoryStream(); - } - - this.ClearQuickAccessToolBar(); - - this.quickAccessToolBar.ItemsChanged -= this.OnQuickAccessItemsChanged; - - foreach (var quickAccessMenuItem in this.QuickAccessItems) - { - this.quickAccessToolBar.QuickAccessItems.Remove(quickAccessMenuItem); - } - } - - this.quickAccessToolBar = this.GetTemplateChild("PART_QuickAccessToolBar") as QuickAccessToolBar; - - if (this.quickAccessToolBar != null) - { - foreach (var quickAccessMenuItem in this.QuickAccessItems) - { - this.quickAccessToolBar.QuickAccessItems.Add(quickAccessMenuItem); - } - - this.quickAccessToolBar.ItemsChanged += this.OnQuickAccessItemsChanged; - - var binding = new Binding("CanQuickAccessLocationChanging") - { - Source = this, - Mode = BindingMode.OneWay - }; - this.quickAccessToolBar.SetBinding(QuickAccessToolBar.CanQuickAccessLocationChangingProperty, binding); - - if (this.quickAccessToolBar.Parent == null) - { - this.AddLogicalChild(this.quickAccessToolBar); - } - - this.quickAccessToolBar.Loaded += this.OnFirstToolbarLoaded; - } - } - - /// - /// Called when the is closed, so that we set it to null and clear the - /// - private void OnOwnerWindowClosed(object sender, EventArgs e) - { - this.DetachFromWindow(); - } - - private void AttachToWindow() - { - this.DetachFromWindow(); - - this.ownerWindow = Window.GetWindow(this); - - if (this.ownerWindow != null) - { - this.ownerWindow.Closed += this.OnOwnerWindowClosed; - this.ownerWindow.SizeChanged += this.OnSizeChanged; - this.ownerWindow.KeyDown += this.OnKeyDown; - - var binding = new Binding("Title") - { - Mode = BindingMode.OneWay, - Source = this.ownerWindow - }; - this.SetBinding(TitleProperty, binding); - } - } - - private void DetachFromWindow() - { - if (this.ownerWindow != null) - { - this.ribbonState.SaveStateToIsolatedStorage(); - - this.ownerWindow.Closed -= this.OnOwnerWindowClosed; - this.ownerWindow.SizeChanged -= this.OnSizeChanged; - this.ownerWindow.KeyDown -= this.OnKeyDown; - - BindingOperations.ClearBinding(this, TitleProperty); - } - - this.ownerWindow = null; - } - - private void OnFirstToolbarLoaded(object sender, RoutedEventArgs e) - { - this.quickAccessToolBar.Loaded -= this.OnFirstToolbarLoaded; - - this.ribbonState.LoadStateFromMemoryStream(); - } - - #endregion - - #region Quick Access Items Managment - - /// - /// Determines whether the given element is in quick access toolbar - /// - /// Element - /// True if element in quick access toolbar - public bool IsInQuickAccessToolBar(UIElement element) - { - if (element == null) - { - return false; - } - - return this.quickAccessElements.ContainsKey(element); - } - - /// - /// Adds the given element to quick access toolbar - /// - /// Element - public void AddToQuickAccessToolBar(UIElement element) - { - if (element == null) - { - return; - } - - if (element is Gallery) - { - element = FindParentRibbonControl(element) as UIElement; - } - - // Do not add menu items without icon. - var menuItem = element as MenuItem; - if (menuItem != null && menuItem.Icon == null) - { - element = FindParentRibbonControl(element) as UIElement; - } - - if (element == null) - { - return; - } - - if (QuickAccessItemsProvider.IsSupported(element) == false) - { - return; - } - - if (this.IsInQuickAccessToolBar(element) == false) - { - Debug.WriteLine("Adding \"{0}\" to QuickAccessToolBar.", element); - - var control = QuickAccessItemsProvider.GetQuickAccessItem(element); - - this.quickAccessElements.Add(element, control); - this.quickAccessToolBar.Items.Add(control); - } - } - - private static IRibbonControl FindParentRibbonControl(DependencyObject element) - { - var parent = LogicalTreeHelper.GetParent(element); - - while (parent != null) - { - var control = parent as IRibbonControl; - if (control != null) - { - return control; - } - - parent = LogicalTreeHelper.GetParent(parent); - } - - return null; - } - - /// - /// Removes the given elements from quick access toolbar - /// - /// Element - public void RemoveFromQuickAccessToolBar(UIElement element) - { - Debug.WriteLine("Removing \"{0}\" from QuickAccessToolBar.", element); - - if (this.IsInQuickAccessToolBar(element)) - { - var quickAccessItem = this.quickAccessElements[element]; - this.quickAccessElements.Remove(element); - this.quickAccessToolBar.Items.Remove(quickAccessItem); - } - } - - /// - /// Clears quick access toolbar - /// - public void ClearQuickAccessToolBar() - { - this.quickAccessElements.Clear(); - if (this.quickAccessToolBar != null) - { - this.quickAccessToolBar.Items.Clear(); - } - } - - #endregion - - #region Event Handling - - // Handles tab control selection changed - private void OnTabControlSelectionChanged(object sender, SelectionChangedEventArgs e) - { - if (this.TabControl != null) - { - this.SelectedTabItem = this.TabControl.SelectedItem as RibbonTabItem; - this.SelectedTabIndex = this.TabControl.SelectedIndex; - } - - if (this.SelectedTabChanged != null) - { - this.SelectedTabChanged(this, e); - } - } - - private void OnLoaded(object sender, RoutedEventArgs e) - { - this.keyTipService.Attach(); - - this.AttachToWindow(); - - this.LoadInitialState(); - } - - private void OnKeyDown(object sender, KeyEventArgs e) - { - if (e.Key == Key.F1 - && Keyboard.Modifiers.HasFlag(ModifierKeys.Control)) - { - if (this.TabControl.HasItems) - { - this.IsMinimized = !this.IsMinimized; - } - } - } - - private void OnUnloaded(object sender, RoutedEventArgs e) - { - this.ribbonState.SaveStateToIsolatedStorage(); - - this.keyTipService.Detach(); - - if (this.ownerWindow != null) - { - this.ownerWindow.SizeChanged -= this.OnSizeChanged; - this.ownerWindow.KeyDown -= this.OnKeyDown; - } - } - - #endregion - - #region Private methods - - private RibbonTabItem GetFirstVisibleItem() - { - return this.Tabs.FirstOrDefault(item => item.Visibility == Visibility.Visible); - } - - private RibbonTabItem GetLastVisibleItem() - { - return this.Tabs.LastOrDefault(item => item.Visibility == Visibility.Visible); - } - - #endregion - - #region State Management - - private void LoadInitialState() - { - if (this.ribbonState.IsStateLoaded) - { - return; - } - - this.ribbonState.LoadStateFromIsolatedStorage(); - - if (this.TabControl != null - && this.TabControl.SelectedIndex == -1 - && this.TabControl.IsMinimized == false) - { - this.TabControl.SelectedIndex = 0; - } - } - - // Handles items changing in QAT - private void OnQuickAccessItemsChanged(object sender, NotifyCollectionChangedEventArgs e) - { - this.ribbonState.SaveStateToMemoryStream(); - } - - // Traverse logical tree and find QAT items, remember paths - private void TraverseLogicalTree(DependencyObject item, string path, IDictionary paths) - { - // Is this item in QAT - var uielement = item as FrameworkElement; - if (uielement != null - && this.quickAccessElements.ContainsKey(uielement)) - { - if (!paths.ContainsKey(uielement)) - { - paths.Add(uielement, path); - } - } - - var children = LogicalTreeHelper.GetChildren(item).Cast().ToArray(); - for (var i = 0; i < children.Length; i++) - { - var child = children[i] as DependencyObject; - if (child == null) - { - continue; - } - - this.TraverseLogicalTree(child, path + i + ",", paths); - } - } - - #endregion - - #region Load from Stream - - // Loads item and add to QAT - private void ParseAndAddToQuickAccessToolBar(string data) - { - var indices = data.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) - .Select(x => int.Parse(x, CultureInfo.InvariantCulture)).ToArray(); - - DependencyObject current = this; - - for (var i = 0; i < indices.Length; i++) - { - var children = LogicalTreeHelper.GetChildren(current).OfType().ToArray(); - var indexIsInvalid = children.Length <= indices[i]; - var item = indexIsInvalid - ? null - : children[indices[i]] as DependencyObject; - - if (item == null) - { - // Path is incorrect - Debug.WriteLine("Error while QAT items loading: one of the paths is invalid"); - return; - } - - current = item; - } - - var result = current as UIElement; - if (result == null - || QuickAccessItemsProvider.IsSupported(result) == false) - { - // Item is invalid - Debug.WriteLine("Error while QAT items loading. Could not add \"{0}\" to QAT.", current); - return; - } - - this.AddToQuickAccessToolBar(result); - } - - #endregion - - #region AutomaticStateManagement Property - - // To temporary suppress automatic management - private bool suppressAutomaticStateManagement; - - /// - /// Gets or sets whether Quick Access ToolBar can - /// save and load its state automatically - /// - public bool AutomaticStateManagement - { - get { return (bool)this.GetValue(AutomaticStateManagementProperty); } - set { this.SetValue(AutomaticStateManagementProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for AutomaticStateManagement. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty AutomaticStateManagementProperty = - DependencyProperty.Register("AutomaticStateManagement", typeof(bool), typeof(Ribbon), new UIPropertyMetadata(true, OnAutoStateManagement, CoerceAutoStateManagement)); - - private static object CoerceAutoStateManagement(DependencyObject d, object basevalue) - { - var ribbon = (Ribbon)d; - if (ribbon.suppressAutomaticStateManagement) - { - return false; - } - - return basevalue; - } - - private static void OnAutoStateManagement(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var ribbon = (Ribbon)d; - if ((bool)e.NewValue) - { - ribbon.LoadInitialState(); - } - } - - #endregion - - private class RibbonState : IDisposable - { - private readonly Ribbon ribbon; - - // Name of the isolated storage file - private string isolatedStorageFileName; - private Stream memoryStream; - - public RibbonState(Ribbon ribbon) - { - this.ribbon = ribbon; - this.memoryStream = new MemoryStream(); - } - - /// - /// Gets or sets whether state is loaded - /// - public bool IsStateLoaded { get; private set; } - - /// - /// Gets name of the isolated storage file - /// - private string IsolatedStorageFileName - { - get - { - if (this.isolatedStorageFileName != null) - { - return this.isolatedStorageFileName; - } - - var stringForHash = ""; - var window = Window.GetWindow(this.ribbon); - - if (window != null) - { - stringForHash += "." + window.GetType().FullName; - - if (string.IsNullOrEmpty(window.Name) == false - && window.Name.Trim().Length > 0) - { - stringForHash += "." + window.Name; - } - } - - if (string.IsNullOrEmpty(this.ribbon.Name) == false - && this.ribbon.Name.Trim().Length > 0) - { - stringForHash += "." + this.ribbon.Name; - } - - this.isolatedStorageFileName = "Fluent.Ribbon.State.2.0." + stringForHash.GetHashCode().ToString("X"); - return this.isolatedStorageFileName; - } - } - - public void SaveStateToMemoryStream() - { - this.memoryStream.Position = 0; - this.SaveState(this.memoryStream); - } - - // Saves to Isolated Storage (in user store for domain) - public void SaveStateToIsolatedStorage() - { - // Check whether automatic save is valid now - if (this.ribbon.AutomaticStateManagement == false) - { - Trace.WriteLine(string.Format("State not saved to isolated storage. Because automatic state management is disabled.")); - return; - } - - if (this.IsStateLoaded == false) - { - Trace.WriteLine(string.Format("State not saved to isolated storage. Because state was not loaded before.")); - return; - } - - try - { - var storage = GetIsolatedStorageFile(); - - using (var stream = new IsolatedStorageFileStream(this.IsolatedStorageFileName, FileMode.Create, FileAccess.Write, storage)) - { - this.SaveState(stream); - } - } - catch (Exception ex) - { - Trace.WriteLine(string.Format("Error while trying to save Ribbon state. Error: {0}", ex)); - } - } - - /// - /// Saves state to the given stream - /// - /// Stream - private void SaveState(Stream stream) - { - // Don't save or load state in design mode - if (DesignerProperties.GetIsInDesignMode(this.ribbon)) - { - return; - } - - var builder = new StringBuilder(); - - var isMinimizedSaveState = this.ribbon.IsMinimized; - - // Save Ribbon State - builder.Append(isMinimizedSaveState.ToString(CultureInfo.InvariantCulture)); - builder.Append(','); - builder.Append(this.ribbon.ShowQuickAccessToolBarAboveRibbon.ToString(CultureInfo.InvariantCulture)); - builder.Append('|'); - - // Save QAT items - var paths = new Dictionary(); - this.ribbon.TraverseLogicalTree(this.ribbon, "", paths); - - // Foreach items and see whether path is found for the item - foreach (var element in this.ribbon.quickAccessElements) - { - string path; - var control = element.Key as FrameworkElement; - - if (control != null - && paths.TryGetValue(control, out path)) - { - builder.Append(path); - builder.Append(';'); - } - else - { - // Item is not found in logical tree, output to debug console - var controlName = (control != null && string.IsNullOrEmpty(control.Name) == false) - ? string.Format(CultureInfo.InvariantCulture, " (name of the control is {0})", control.Name) - : string.Empty; - - Debug.WriteLine("Control " + element.Key.GetType().Name + " is not found in logical tree during QAT saving" + controlName); - } - } - - var writer = new StreamWriter(stream); - writer.Write(builder.ToString()); - - writer.Flush(); - } - - public void LoadStateFromMemoryStream() - { - this.memoryStream.Position = 0; - this.LoadState(this.memoryStream); - } - - /// - /// Loads the State from Isolated Storage (in user store for domain) - /// - /// - /// Sets after it's finished to prevent a race condition with saving the state to the MemoryStream. - /// - public void LoadStateFromIsolatedStorage() - { - // Don't save or load state in design mode - if (DesignerProperties.GetIsInDesignMode(this.ribbon)) - { - Trace.WriteLine(string.Format("State not loaded from isolated storage. Because we are in design mode.")); - this.IsStateLoaded = true; - return; - } - - if (this.ribbon.AutomaticStateManagement == false) - { - this.IsStateLoaded = true; - Trace.WriteLine(string.Format("State not loaded from isolated storage. Because automatic state management is disabled.")); - return; - } - - try - { - var storage = GetIsolatedStorageFile(); - if (FileExists(storage, this.IsolatedStorageFileName)) - { - using (var stream = new IsolatedStorageFileStream(this.IsolatedStorageFileName, FileMode.Open, FileAccess.Read, storage)) - { - this.LoadState(stream); - - // Copy loaded state to MemoryStream for temporary storage. - // Temporary storage is used for style changes etc. so we can apply the current state again. - stream.Position = 0; - this.memoryStream.Position = 0; - stream.CopyTo(this.memoryStream); - } - } - } - catch (Exception ex) - { - Trace.WriteLine(string.Format("Error while trying to load Ribbon state. Error: {0}", ex)); - } - - this.IsStateLoaded = true; - } - - /// - /// Loads state from the given stream - /// - /// Stream - private void LoadState(Stream stream) - { - this.ribbon.suppressAutomaticStateManagement = true; - - var reader = new StreamReader(stream); - var splitted = reader.ReadToEnd().Split('|'); - - if (splitted.Length != 2) - { - return; - } - - // Load Ribbon State - var ribbonProperties = splitted[0].Split(','); - - var isMinimized = bool.Parse(ribbonProperties[0]); - - this.ribbon.IsMinimized = isMinimized; - - this.ribbon.ShowQuickAccessToolBarAboveRibbon = bool.Parse(ribbonProperties[1]); - - // Load items - var items = splitted[1].Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); - - if (this.ribbon.quickAccessToolBar != null) - { - this.ribbon.quickAccessToolBar.Items.Clear(); - } - - this.ribbon.quickAccessElements.Clear(); - - for (var i = 0; i < items.Length; i++) - { - this.ribbon.ParseAndAddToQuickAccessToolBar(items[i]); - } - - // Since application is not fully loaded we have to delay the refresh - this.ribbon.RunInDispatcherAsync(this.ribbon.QuickAccessToolBar.Refresh, DispatcherPriority.Background); - - // Sync QAT menu items - foreach (var menuItem in this.ribbon.QuickAccessItems) - { - menuItem.IsChecked = this.ribbon.IsInQuickAccessToolBar(menuItem.Target); - } - - this.ribbon.suppressAutomaticStateManagement = false; - } - - // Determinates whether the given file exists in the given storage - private static bool FileExists(IsolatedStorageFile storage, string fileName) - { - var files = storage.GetFileNames(fileName); - return files.Length != 0; - } - - // Gets a proper isolated storage file - private static IsolatedStorageFile GetIsolatedStorageFile() - { - try - { - return IsolatedStorageFile.GetUserStoreForDomain(); - } - catch - { - return IsolatedStorageFile.GetUserStoreForAssembly(); - } - } - - /// - /// Resets automatically saved state - /// - public static void ResetState() - { - var storage = GetIsolatedStorageFile(); - - foreach (var filename in storage.GetFileNames("*Fluent.Ribbon.State*")) - { - storage.DeleteFile(filename); - } - } - - private void Dispose(bool disposing) - { - if (disposing == false) - { - return; - } - - if (this.memoryStream != null) - { - this.memoryStream.Dispose(); - } - - this.memoryStream = Stream.Null; - } - - public void Dispose() - { - this.Dispose(true); - GC.SuppressFinalize(this); - } - } - } +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO; +using System.IO.IsolatedStorage; +using System.Linq; +using System.Text; +using System.Threading; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Input; +using System.Windows.Markup; + +namespace Fluent +{ + using System.ComponentModel; + using System.Windows.Threading; + using Fluent.Extensions; + + // TODO: improve style parts naming & using + + /// + /// Represents the main Ribbon control which consists of multiple tabs, each of which + /// containing groups of controls. The Ribbon also provides improved context + /// menus, enhanced screen tips, and keyboard shortcuts. + /// + [ContentProperty("Tabs")] + [SuppressMessage("Microsoft.Maintainability", "CA1506")] + [SuppressMessage("Microsoft.Design", "CA1001")] + public class Ribbon : Control + { + private readonly RibbonState ribbonState; + + #region Localization + + // Localizable properties + static readonly RibbonLocalization localization = new RibbonLocalization(); + + /// + /// Gets localizable properties + /// + public static RibbonLocalization Localization + { + get { return localization; } + } + + #endregion + + #region Constants + + /// + /// Minimal width of ribbon parent window + /// + public const double MinimalVisibleWidth = 300; + /// + /// Minimal height of ribbon parent window + /// + public const double MinimalVisibleHeight = 250; + + #endregion + + #region ContextMenu + + private static readonly Dictionary contextMenus = new Dictionary(); + + /// + /// Context menu for ribbon in current thread + /// + public static System.Windows.Controls.ContextMenu RibbonContextMenu + { + get + { + if (!contextMenus.ContainsKey(Thread.CurrentThread.ManagedThreadId)) InitRibbonContextMenu(); + return contextMenus[Thread.CurrentThread.ManagedThreadId]; + } + } + + // Context menu owner ribbon + private static Ribbon contextMenuOwner; + + // Context menu items + private static Dictionary addToQuickAccessMenuItemDictionary = new Dictionary(); + private static System.Windows.Controls.MenuItem addToQuickAccessMenuItem + { + get { return addToQuickAccessMenuItemDictionary[Thread.CurrentThread.ManagedThreadId]; } + } + private static Dictionary addGroupToQuickAccessMenuItemDictionary = new Dictionary(); + private static System.Windows.Controls.MenuItem addGroupToQuickAccessMenuItem + { + get { return addGroupToQuickAccessMenuItemDictionary[Thread.CurrentThread.ManagedThreadId]; } + } + private static Dictionary addMenuToQuickAccessMenuItemDictionary = new Dictionary(); + private static System.Windows.Controls.MenuItem addMenuToQuickAccessMenuItem + { + get { return addMenuToQuickAccessMenuItemDictionary[Thread.CurrentThread.ManagedThreadId]; } + } + private static Dictionary addGalleryToQuickAccessMenuItemDictionary = new Dictionary(); + private static System.Windows.Controls.MenuItem addGalleryToQuickAccessMenuItem + { + get { return addGalleryToQuickAccessMenuItemDictionary[Thread.CurrentThread.ManagedThreadId]; } + } + private static Dictionary removeFromQuickAccessMenuItemDictionary = new Dictionary(); + private static System.Windows.Controls.MenuItem removeFromQuickAccessMenuItem + { + get { return removeFromQuickAccessMenuItemDictionary[Thread.CurrentThread.ManagedThreadId]; } + } + private static Dictionary showQuickAccessToolbarBelowTheRibbonMenuItemDictionary = new Dictionary(); + private static System.Windows.Controls.MenuItem showQuickAccessToolbarBelowTheRibbonMenuItem + { + get { return showQuickAccessToolbarBelowTheRibbonMenuItemDictionary[Thread.CurrentThread.ManagedThreadId]; } + } + private static Dictionary showQuickAccessToolbarAboveTheRibbonMenuItemDictionary = new Dictionary(); + private static System.Windows.Controls.MenuItem showQuickAccessToolbarAboveTheRibbonMenuItem + { + get { return showQuickAccessToolbarAboveTheRibbonMenuItemDictionary[Thread.CurrentThread.ManagedThreadId]; } + } + private static Dictionary minimizeTheRibbonMenuItemDictionary = new Dictionary(); + private static System.Windows.Controls.MenuItem minimizeTheRibbonMenuItem + { + get { return minimizeTheRibbonMenuItemDictionary[Thread.CurrentThread.ManagedThreadId]; } + } + private static Dictionary customizeQuickAccessToolbarMenuItemDictionary = new Dictionary(); + private static System.Windows.Controls.MenuItem customizeQuickAccessToolbarMenuItem + { + get { return customizeQuickAccessToolbarMenuItemDictionary[Thread.CurrentThread.ManagedThreadId]; } + } + private static Dictionary customizeTheRibbonMenuItemDictionary = new Dictionary(); + private static System.Windows.Controls.MenuItem customizeTheRibbonMenuItem + { + get { return customizeTheRibbonMenuItemDictionary[Thread.CurrentThread.ManagedThreadId]; } + } + private static Dictionary firstSeparatorDictionary = new Dictionary(); + private static Separator firstSeparator + { + get { return firstSeparatorDictionary[Thread.CurrentThread.ManagedThreadId]; } + } + private static Dictionary secondSeparatorDictionary = new Dictionary(); + private static Separator secondSeparator + { + get { return secondSeparatorDictionary[Thread.CurrentThread.ManagedThreadId]; } + } + + // Initialize ribbon context menu + private static void InitRibbonContextMenu() + { + contextMenus.Add(Thread.CurrentThread.ManagedThreadId, new System.Windows.Controls.ContextMenu()); + RibbonContextMenu.Opened += OnContextMenuOpened; + + // Add to quick access toolbar + addToQuickAccessMenuItemDictionary.Add(Thread.CurrentThread.ManagedThreadId, new System.Windows.Controls.MenuItem { Command = AddToQuickAccessCommand }); + RibbonContextMenu.Items.Add(addToQuickAccessMenuItem); + RibbonControl.Bind(Localization, addToQuickAccessMenuItem, "RibbonContextMenuAddItem", HeaderedItemsControl.HeaderProperty, BindingMode.OneWay); + RibbonControl.Bind(RibbonContextMenu, addToQuickAccessMenuItem, "PlacementTarget", System.Windows.Controls.MenuItem.CommandParameterProperty, BindingMode.OneWay); + + // Add group to quick access toolbar + addGroupToQuickAccessMenuItemDictionary.Add(Thread.CurrentThread.ManagedThreadId, new System.Windows.Controls.MenuItem { Command = AddToQuickAccessCommand }); + RibbonContextMenu.Items.Add(addGroupToQuickAccessMenuItem); + RibbonControl.Bind(Localization, addGroupToQuickAccessMenuItem, "RibbonContextMenuAddGroup", HeaderedItemsControl.HeaderProperty, BindingMode.OneWay); + RibbonControl.Bind(RibbonContextMenu, addGroupToQuickAccessMenuItem, "PlacementTarget", System.Windows.Controls.MenuItem.CommandParameterProperty, BindingMode.OneWay); + + // Add menu item to quick access toolbar + addMenuToQuickAccessMenuItemDictionary.Add(Thread.CurrentThread.ManagedThreadId, new System.Windows.Controls.MenuItem { Command = AddToQuickAccessCommand }); + RibbonContextMenu.Items.Add(addMenuToQuickAccessMenuItem); + RibbonControl.Bind(Localization, addMenuToQuickAccessMenuItem, "RibbonContextMenuAddMenu", HeaderedItemsControl.HeaderProperty, BindingMode.OneWay); + RibbonControl.Bind(RibbonContextMenu, addMenuToQuickAccessMenuItem, "PlacementTarget", System.Windows.Controls.MenuItem.CommandParameterProperty, BindingMode.OneWay); + + // Add gallery to quick access toolbar + addGalleryToQuickAccessMenuItemDictionary.Add(Thread.CurrentThread.ManagedThreadId, new System.Windows.Controls.MenuItem { Command = AddToQuickAccessCommand }); + RibbonContextMenu.Items.Add(addGalleryToQuickAccessMenuItem); + RibbonControl.Bind(Localization, addGalleryToQuickAccessMenuItem, "RibbonContextMenuAddGallery", HeaderedItemsControl.HeaderProperty, BindingMode.OneWay); + RibbonControl.Bind(RibbonContextMenu, addGalleryToQuickAccessMenuItem, "PlacementTarget", System.Windows.Controls.MenuItem.CommandParameterProperty, BindingMode.OneWay); + + // Remove from quick access toolbar + removeFromQuickAccessMenuItemDictionary.Add(Thread.CurrentThread.ManagedThreadId, new System.Windows.Controls.MenuItem { Command = RemoveFromQuickAccessCommand }); + RibbonContextMenu.Items.Add(removeFromQuickAccessMenuItem); + RibbonControl.Bind(Localization, removeFromQuickAccessMenuItem, "RibbonContextMenuRemoveItem", HeaderedItemsControl.HeaderProperty, BindingMode.OneWay); + RibbonControl.Bind(RibbonContextMenu, removeFromQuickAccessMenuItem, "PlacementTarget", System.Windows.Controls.MenuItem.CommandParameterProperty, BindingMode.OneWay); + + // Separator + firstSeparatorDictionary.Add(Thread.CurrentThread.ManagedThreadId, new Separator()); + RibbonContextMenu.Items.Add(firstSeparator); + + // Customize quick access toolbar + customizeQuickAccessToolbarMenuItemDictionary.Add(Thread.CurrentThread.ManagedThreadId, new System.Windows.Controls.MenuItem { Command = CustomizeQuickAccessToolbarCommand }); + RibbonContextMenu.Items.Add(customizeQuickAccessToolbarMenuItem); + RibbonControl.Bind(Localization, customizeQuickAccessToolbarMenuItem, "RibbonContextMenuCustomizeQuickAccessToolBar", HeaderedItemsControl.HeaderProperty, BindingMode.OneWay); + RibbonControl.Bind(RibbonContextMenu, customizeQuickAccessToolbarMenuItem, "PlacementTarget", System.Windows.Controls.MenuItem.CommandParameterProperty, BindingMode.OneWay); + + // Show quick access below the ribbon + showQuickAccessToolbarBelowTheRibbonMenuItemDictionary.Add(Thread.CurrentThread.ManagedThreadId, new System.Windows.Controls.MenuItem { Command = ShowQuickAccessBelowCommand }); + RibbonContextMenu.Items.Add(showQuickAccessToolbarBelowTheRibbonMenuItem); + RibbonControl.Bind(Localization, showQuickAccessToolbarBelowTheRibbonMenuItem, "RibbonContextMenuShowBelow", HeaderedItemsControl.HeaderProperty, BindingMode.OneWay); + RibbonControl.Bind(RibbonContextMenu, showQuickAccessToolbarBelowTheRibbonMenuItem, "PlacementTarget", System.Windows.Controls.MenuItem.CommandParameterProperty, BindingMode.OneWay); + + // Show quick access above the ribbon + showQuickAccessToolbarAboveTheRibbonMenuItemDictionary.Add(Thread.CurrentThread.ManagedThreadId, new System.Windows.Controls.MenuItem { Command = ShowQuickAccessAboveCommand }); + RibbonContextMenu.Items.Add(showQuickAccessToolbarAboveTheRibbonMenuItem); + RibbonControl.Bind(Localization, showQuickAccessToolbarAboveTheRibbonMenuItem, "RibbonContextMenuShowAbove", HeaderedItemsControl.HeaderProperty, BindingMode.OneWay); + RibbonControl.Bind(RibbonContextMenu, showQuickAccessToolbarAboveTheRibbonMenuItem, "PlacementTarget", System.Windows.Controls.MenuItem.CommandParameterProperty, BindingMode.OneWay); + + // Separator + secondSeparatorDictionary.Add(Thread.CurrentThread.ManagedThreadId, new Separator()); + RibbonContextMenu.Items.Add(secondSeparator); + + // Customize the ribbon + customizeTheRibbonMenuItemDictionary.Add(Thread.CurrentThread.ManagedThreadId, new System.Windows.Controls.MenuItem { Command = CustomizeTheRibbonCommand }); + RibbonContextMenu.Items.Add(customizeTheRibbonMenuItem); + RibbonControl.Bind(Localization, customizeTheRibbonMenuItem, "RibbonContextMenuCustomizeRibbon", HeaderedItemsControl.HeaderProperty, BindingMode.OneWay); + RibbonControl.Bind(RibbonContextMenu, customizeTheRibbonMenuItem, "PlacementTarget", System.Windows.Controls.MenuItem.CommandParameterProperty, BindingMode.OneWay); + + // Minimize the ribbon + minimizeTheRibbonMenuItemDictionary.Add(Thread.CurrentThread.ManagedThreadId, new System.Windows.Controls.MenuItem { Command = ToggleMinimizeTheRibbonCommand }); + RibbonContextMenu.Items.Add(minimizeTheRibbonMenuItem); + RibbonControl.Bind(Localization, minimizeTheRibbonMenuItem, "RibbonContextMenuMinimizeRibbon", HeaderedItemsControl.HeaderProperty, BindingMode.OneWay); + RibbonControl.Bind(RibbonContextMenu, minimizeTheRibbonMenuItem, "PlacementTarget", System.Windows.Controls.MenuItem.CommandParameterProperty, BindingMode.OneWay); + } + + /// + /// Invoked whenever an unhandled routed event reaches this class in its route. Implement this method to add class handling for this event. + /// + /// The that contains the event data. + protected override void OnContextMenuOpening(ContextMenuEventArgs e) + { + contextMenuOwner = this; + base.OnContextMenuOpening(e); + } + + /// + /// Invoked whenever an unhandled routed event reaches this class in its route. Implement this method to add class handling for this event. + /// + /// Provides data about the event. + protected override void OnContextMenuClosing(ContextMenuEventArgs e) + { + contextMenuOwner = null; + base.OnContextMenuClosing(e); + } + + // Occurs when context menu is opening + private static void OnContextMenuOpened(object sender, RoutedEventArgs e) + { + var ribbon = contextMenuOwner; + + if (RibbonContextMenu == null + || ribbon == null) + { + return; + } + + addToQuickAccessMenuItem.CommandTarget = ribbon; + addGroupToQuickAccessMenuItem.CommandTarget = ribbon; + addMenuToQuickAccessMenuItem.CommandTarget = ribbon; + addGalleryToQuickAccessMenuItem.CommandTarget = ribbon; + removeFromQuickAccessMenuItem.CommandTarget = ribbon; + customizeQuickAccessToolbarMenuItem.CommandTarget = ribbon; + customizeTheRibbonMenuItem.CommandTarget = ribbon; + minimizeTheRibbonMenuItem.CommandTarget = ribbon; + showQuickAccessToolbarBelowTheRibbonMenuItem.CommandTarget = ribbon; + showQuickAccessToolbarAboveTheRibbonMenuItem.CommandTarget = ribbon; + + // Hide items for ribbon controls + addToQuickAccessMenuItem.Visibility = Visibility.Collapsed; + addGroupToQuickAccessMenuItem.Visibility = Visibility.Collapsed; + addMenuToQuickAccessMenuItem.Visibility = Visibility.Collapsed; + addGalleryToQuickAccessMenuItem.Visibility = Visibility.Collapsed; + removeFromQuickAccessMenuItem.Visibility = Visibility.Collapsed; + firstSeparator.Visibility = Visibility.Collapsed; + + // Hide customize quick access menu item + customizeQuickAccessToolbarMenuItem.Visibility = Visibility.Collapsed; + secondSeparator.Visibility = Visibility.Collapsed; + + // Set minimize the ribbon menu item state + minimizeTheRibbonMenuItem.IsChecked = ribbon.IsMinimized; + + // Set customize the ribbon menu item visibility + if (ribbon.CanCustomizeRibbon) + { + customizeTheRibbonMenuItem.Visibility = Visibility.Visible; + } + else + { + customizeTheRibbonMenuItem.Visibility = Visibility.Collapsed; + } + + // Hide quick access position menu items + showQuickAccessToolbarBelowTheRibbonMenuItem.Visibility = Visibility.Collapsed; + showQuickAccessToolbarAboveTheRibbonMenuItem.Visibility = Visibility.Collapsed; + + // If quick access toolbar is visible show + if (ribbon.IsQuickAccessToolBarVisible) + { + // Set quick access position menu items visibility + if (ribbon.CanQuickAccessLocationChanging) + { + if (ribbon.ShowQuickAccessToolBarAboveRibbon) + { + showQuickAccessToolbarBelowTheRibbonMenuItem.Visibility = Visibility.Visible; + } + else + { + showQuickAccessToolbarAboveTheRibbonMenuItem.Visibility = Visibility.Visible; + } + } + + if (ribbon.CanCustomizeQuickAccessToolBar) + { + customizeQuickAccessToolbarMenuItem.Visibility = Visibility.Visible; + } + + if (ribbon.CanQuickAccessLocationChanging + || ribbon.CanCustomizeQuickAccessToolBar) + { + secondSeparator.Visibility = Visibility.Visible; + } + else + { + secondSeparator.Visibility = Visibility.Collapsed; + } + + if (ribbon.CanCustomizeQuickAccessToolBarItems) + { + // Gets control that raise menu opened + var control = RibbonContextMenu.PlacementTarget; + AddToQuickAccessCommand.CanExecute(null, control); + RemoveFromQuickAccessCommand.CanExecute(null, control); + + //Debug.WriteLine("Menu opened on "+control); + if (control != null) + { + firstSeparator.Visibility = Visibility.Visible; + + // Check for value because remove is only possible in the context menu of items in QA which represent the value for QA-items + if (ribbon.quickAccessElements.ContainsValue(control)) + { + // Control is on quick access + removeFromQuickAccessMenuItem.Visibility = Visibility.Visible; + } + else if (control is System.Windows.Controls.MenuItem) + { + // Control is menu item + addMenuToQuickAccessMenuItem.Visibility = Visibility.Visible; + } + else if ((control is Gallery) || + (control is InRibbonGallery)) + { + // Control is gallery + addGalleryToQuickAccessMenuItem.Visibility = Visibility.Visible; + } + else if (control is RibbonGroupBox) + { + // Control is group box + addGroupToQuickAccessMenuItem.Visibility = Visibility.Visible; + } + else if (control is IQuickAccessItemProvider) + { + // Its other control + addToQuickAccessMenuItem.Visibility = Visibility.Visible; + } + else + { + firstSeparator.Visibility = Visibility.Collapsed; + } + } + } + } + } + + #endregion + + #region Events + + /// + /// Occurs when selected tab has been changed (be aware that SelectedTab can be null) + /// + public event SelectionChangedEventHandler SelectedTabChanged; + + /// + /// Occurs when customize the ribbon + /// + public event EventHandler CustomizeTheRibbon; + + /// + /// Occurs when customize quick access toolbar + /// + public event EventHandler CustomizeQuickAccessToolbar; + + /// + /// Occurs when IsMinimized property is changing + /// + public event DependencyPropertyChangedEventHandler IsMinimizedChanged; + + /// + /// Occurs when IsCollapsed property is changing + /// + public event DependencyPropertyChangedEventHandler IsCollapsedChanged; + + #endregion + + #region Fields + + // Collection of contextual tab groups + private ObservableCollection contextualGroups; + + // Collection of tabs + private ObservableCollection tabs; + + // Collection of toolbar items + private ObservableCollection toolBarItems; + + // Ribbon quick access toolbar + private QuickAccessToolBar quickAccessToolBar; + + // Ribbon layout root + private Panel layoutRoot; + + // Handles F10, Alt and so on + readonly KeyTipService keyTipService; + + // Collection of quickaccess menu items + private ObservableCollection quickAccessItems; + + // Currently added in QAT items + private readonly Dictionary quickAccessElements = new Dictionary(); + + private Window ownerWindow; + + #endregion + + #region Properties + + #region Menu + + /// + /// Gets or sets file menu control (can be application menu button, backstage button and so on) + /// + public UIElement Menu + { + get { return (UIElement)this.GetValue(MenuProperty); } + set { this.SetValue(MenuProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Button. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty MenuProperty = + DependencyProperty.Register("Menu", typeof(UIElement), typeof(Ribbon), new UIPropertyMetadata(null, OnApplicationMenuChanged)); + + static void OnApplicationMenuChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + Ribbon ribbon = (Ribbon)d; + if (e.OldValue != null) ribbon.RemoveLogicalChild(e.OldValue); + if (e.NewValue != null) ribbon.AddLogicalChild(e.NewValue); + } + + #endregion + + /// + /// Window title + /// + public string Title + { + get { return (string)this.GetValue(TitleProperty); } + set { this.SetValue(TitleProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Title. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty TitleProperty = + DependencyProperty.Register("Title", typeof(string), typeof(Ribbon), new UIPropertyMetadata("", OnTitleChanged)); + + private static void OnTitleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var ribbon = d as Ribbon; + + if (ribbon != null + && ribbon.TitleBar != null) + { + ribbon.TitleBar.InvalidateMeasure(); + } + } + + /// + /// Gets or sets selected tab item + /// + public RibbonTabItem SelectedTabItem + { + get { return (RibbonTabItem)this.GetValue(SelectedTabItemProperty); } + set { this.SetValue(SelectedTabItemProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for SelectedTabItem. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty SelectedTabItemProperty = + DependencyProperty.Register("SelectedTabItem", typeof(RibbonTabItem), typeof(Ribbon), new UIPropertyMetadata(null, OnSelectedTabItemChanged)); + + private static void OnSelectedTabItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var ribbon = (Ribbon)d; + if (ribbon.TabControl != null) + { + ribbon.TabControl.SelectedItem = e.NewValue; + } + + var selectedItem = e.NewValue as RibbonTabItem; + + if (selectedItem != null + && ribbon.Tabs.Contains(selectedItem)) + { + ribbon.SelectedTabIndex = ribbon.Tabs.IndexOf(selectedItem); + } + else + { + ribbon.SelectedTabIndex = -1; + } + } + + /// + /// Gets or sets selected tab index + /// + public int SelectedTabIndex + { + get { return (int)this.GetValue(SelectedTabIndexProperty); } + set { this.SetValue(SelectedTabIndexProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for SelectedTabindex. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty SelectedTabIndexProperty = + DependencyProperty.Register("SelectedTabIndex", typeof(int), typeof(Ribbon), new UIPropertyMetadata(-1, OnSelectedTabIndexChanged)); + + private static void OnSelectedTabIndexChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var ribbon = (Ribbon)d; + var selectedIndex = (int)e.NewValue; + + if (ribbon.TabControl != null) + { + ribbon.TabControl.SelectedIndex = selectedIndex; + } + + if (selectedIndex >= 0 + && selectedIndex < ribbon.Tabs.Count) + { + ribbon.SelectedTabItem = ribbon.Tabs[selectedIndex]; + } + else + { + ribbon.SelectedTabItem = null; + } + } + + /// + /// Gets the first visible TabItem + /// + public RibbonTabItem FirstVisibleItem + { + get + { + return this.GetFirstVisibleItem(); + } + } + + /// + /// Gets the last visible TabItem + /// + public RibbonTabItem LastVisibleItem + { + get + { + return this.GetLastVisibleItem(); + } + } + + /// + /// Gets the list of currently active quick access elements. + /// + protected Dictionary QuickAccessElements + { + get + { + return this.quickAccessElements; + } + } + + /// + /// Gets ribbon titlebar + /// + internal RibbonTitleBar TitleBar { get; private set; } + + /// + /// Gets the Ribbon tab control + /// + internal RibbonTabControl TabControl { get; private set; } + + /// + /// Gets or sets whether quick access toolbar showes above ribbon + /// + public bool ShowQuickAccessToolBarAboveRibbon + { + get { return (bool)this.GetValue(ShowQuickAccessToolBarAboveRibbonProperty); } + set { this.SetValue(ShowQuickAccessToolBarAboveRibbonProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for ShowAboveRibbon. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty ShowQuickAccessToolBarAboveRibbonProperty = + DependencyProperty.Register("ShowQuickAccessToolBarAboveRibbon", typeof(bool), typeof(Ribbon), new UIPropertyMetadata(true, OnShowQuickAccesToolBarAboveRibbonChanged)); + + /// + /// Handles ShowQuickAccessToolBarAboveRibbon property changed + /// + /// Object + /// The event data + private static void OnShowQuickAccesToolBarAboveRibbonChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var ribbon = (Ribbon)d; + if (ribbon.TitleBar != null) + { + ribbon.TitleBar.InvalidateMeasure(); + } + + ribbon.ribbonState.SaveStateToMemoryStream(); + } + + /// + /// Gets collection of contextual tab groups + /// + public ObservableCollection ContextualGroups + { + get + { + if (this.contextualGroups == null) + { + this.contextualGroups = new ObservableCollection(); + this.contextualGroups.CollectionChanged += this.OnContextualGroupsCollectionChanged; + } + + return this.contextualGroups; + } + } + + /// + /// Handles collection of contextual tab groups ghanges + /// + /// Sender + /// The event data + private void OnContextualGroupsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + for (int i = 0; i < e.NewItems.Count; i++) + { + if (this.TitleBar != null) this.TitleBar.Items.Insert(e.NewStartingIndex + i, e.NewItems[i]); + } + break; + + case NotifyCollectionChangedAction.Remove: + foreach (object item in e.OldItems) + { + if (this.TitleBar != null) this.TitleBar.Items.Remove(item); + } + break; + + case NotifyCollectionChangedAction.Replace: + foreach (object item in e.OldItems) + { + if (this.TitleBar != null) this.TitleBar.Items.Remove(item); + } + foreach (object item in e.NewItems) + { + if (this.TitleBar != null) this.TitleBar.Items.Add(item); + } + break; + case NotifyCollectionChangedAction.Reset: + if (this.TitleBar != null) this.TitleBar.Items.Clear(); + break; + } + + } + + /// + /// gets collection of ribbon tabs + /// + public ObservableCollection Tabs + { + get + { + if (this.tabs == null) + { + this.tabs = new ObservableCollection(); + this.tabs.CollectionChanged += this.OnTabsCollectionChanged; + } + + return this.tabs; + } + } + + /// + /// Handles collection of ribbon tabs changed + /// + /// Sender + /// The event data + private void OnTabsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + if (this.TabControl == null) + { + return; + } + + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + for (var i = 0; i < e.NewItems.Count; i++) + { + this.TabControl.Items.Insert(e.NewStartingIndex + i, e.NewItems[i]); + } + break; + + case NotifyCollectionChangedAction.Remove: + foreach (var item in e.OldItems) + { + this.TabControl.Items.Remove(item); + } + break; + + case NotifyCollectionChangedAction.Replace: + foreach (var item in e.OldItems) + { + this.TabControl.Items.Remove(item); + } + + foreach (var item in e.NewItems) + { + this.TabControl.Items.Add(item); + } + break; + + case NotifyCollectionChangedAction.Reset: + this.TabControl.Items.Clear(); + break; + } + } + + /// + /// Gets collection of toolbar items + /// + public ObservableCollection ToolBarItems + { + get + { + if (this.toolBarItems == null) + { + this.toolBarItems = new ObservableCollection(); + this.toolBarItems.CollectionChanged += this.OnToolbarItemsCollectionChanged; + } + + return this.toolBarItems; + } + } + + /// + /// Handles collection of toolbar items changes + /// + /// Sender + /// The event data + private void OnToolbarItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + for (int i = 0; i < e.NewItems.Count; i++) + { + if (this.TabControl != null) + this.TabControl.ToolBarItems.Insert(e.NewStartingIndex + i, (UIElement)e.NewItems[i]); + } + break; + + case NotifyCollectionChangedAction.Remove: + foreach (object item in e.OldItems) + { + if (this.TabControl != null) + this.TabControl.ToolBarItems.Remove(item as UIElement); + } + break; + + case NotifyCollectionChangedAction.Replace: + foreach (object item in e.OldItems) + { + if (this.TabControl != null) + this.TabControl.ToolBarItems.Remove(item as UIElement); + } + foreach (object item in e.NewItems) + { + if (this.TabControl != null) + this.TabControl.ToolBarItems.Add(item as UIElement); + } + break; + } + + } + + /// + /// Gets quick access toolbar associated with the ribbon + /// + internal QuickAccessToolBar QuickAccessToolBar + { + get { return this.quickAccessToolBar; } + } + + /// + /// Gets an enumerator for logical child elements of this element. + /// + protected override IEnumerator LogicalChildren + { + get + { + if (this.layoutRoot != null) + { + yield return this.layoutRoot; + } + + if (this.Menu != null) + { + yield return this.Menu; + } + + if (this.quickAccessToolBar != null) + { + yield return this.quickAccessToolBar; + } + + if (this.TabControl != null + && this.TabControl.ToolbarPanel != null) + { + yield return this.TabControl.ToolbarPanel; + } + } + } + + /// + /// Gets collection of quick access menu items + /// + public ObservableCollection QuickAccessItems + { + get + { + if (this.quickAccessItems == null) + { + this.quickAccessItems = new ObservableCollection(); + this.quickAccessItems.CollectionChanged += this.OnQuickAccessItemsCollectionChanged; + } + + return this.quickAccessItems; + } + } + + /// + /// Handles collection of quick access menu items changes + /// + /// Sender + /// The event data + private void OnQuickAccessItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + for (var i = 0; i < e.NewItems.Count; i++) + { + var menuItem = (QuickAccessMenuItem)e.NewItems[i]; + if (this.quickAccessToolBar != null) + { + this.quickAccessToolBar.QuickAccessItems.Insert(e.NewStartingIndex + i, menuItem); + } + menuItem.Ribbon = this; + } + break; + + case NotifyCollectionChangedAction.Remove: + foreach (var item in e.OldItems.OfType()) + { + var menuItem = item; + if (this.quickAccessToolBar != null) + { + this.quickAccessToolBar.QuickAccessItems.Remove(menuItem); + } + menuItem.Ribbon = null; + } + break; + + case NotifyCollectionChangedAction.Replace: + foreach (var item in e.OldItems.OfType()) + { + var menuItem = item; + if (this.quickAccessToolBar != null) + { + this.quickAccessToolBar.QuickAccessItems.Remove(menuItem); + } + menuItem.Ribbon = null; + } + foreach (var item in e.NewItems.OfType()) + { + var menuItem = item; + if (this.quickAccessToolBar != null) + { + this.quickAccessToolBar.QuickAccessItems.Add(menuItem); + } + menuItem.Ribbon = this; + } + break; + } + } + + /// + /// Gets or sets whether Customize Quick Access Toolbar menu item is shown + /// + public bool CanCustomizeQuickAccessToolBar + { + get { return (bool)this.GetValue(CanCustomizeQuickAccessToolBarProperty); } + set { this.SetValue(CanCustomizeQuickAccessToolBarProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for CanCustomizeQuickAccessToolBar. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty CanCustomizeQuickAccessToolBarProperty = + DependencyProperty.Register("CanCustomizeQuickAccessToolBar", typeof(bool), + typeof(Ribbon), new UIPropertyMetadata(false)); + + /// + /// Gets or sets whether items can be added or removed from the quick access toolbar by users. + /// + public bool CanCustomizeQuickAccessToolBarItems + { + get { return (bool)this.GetValue(CanCustomizeQuickAccessToolBarItemsProperty); } + set { this.SetValue(CanCustomizeQuickAccessToolBarItemsProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for CanCustomizeQuickAccessToolBarItems. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty CanCustomizeQuickAccessToolBarItemsProperty = + DependencyProperty.Register("CanCustomizeQuickAccessToolBarItems", typeof(bool), typeof(Ribbon), new PropertyMetadata(true)); + + /// + /// Gets or sets whether Customize Ribbon menu item is shown + /// + public bool CanCustomizeRibbon + { + get { return (bool)this.GetValue(CanCustomizeRibbonProperty); } + set { this.SetValue(CanCustomizeRibbonProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for CanCustomizeRibbon. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty CanCustomizeRibbonProperty = + DependencyProperty.Register("CanCustomizeRibbon", typeof(bool), + typeof(Ribbon), new UIPropertyMetadata(false)); + + + /// + /// Gets or sets whether ribbon is minimized + /// + public bool IsMinimized + { + get { return (bool)this.GetValue(IsMinimizedProperty); } + set { this.SetValue(IsMinimizedProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for IsMinimized. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IsMinimizedProperty = + DependencyProperty.Register("IsMinimized", typeof(bool), + typeof(Ribbon), new UIPropertyMetadata(false, OnIsMinimizedChanged)); + + private static void OnIsMinimizedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var ribbon = (Ribbon)d; + + if (ribbon.IsMinimizedChanged != null) + { + ribbon.IsMinimizedChanged(ribbon, e); + } + } + + /// + /// Gets or sets the height of the gap between the ribbon and the content + /// + public double ContentGapHeight + { + get { return (double)this.GetValue(ContentGapHeightProperty); } + set { this.SetValue(ContentGapHeightProperty, value); } + } + + /// + /// DependencyProperty for + /// + public static readonly DependencyProperty ContentGapHeightProperty = + DependencyProperty.Register("ContentGapHeight", typeof(double), typeof(Ribbon), new UIPropertyMetadata(5D)); + + // todo check if IsCollapsed and IsAutomaticCollapseEnabled should be reduced to one shared property for RibbonWindow and Ribbon + /// + /// Gets whether ribbon is collapsed + /// + public bool IsCollapsed + { + get { return (bool)this.GetValue(IsCollapsedProperty); } + set { this.SetValue(IsCollapsedProperty, value); } + } + + /// + /// DependencyProperty for + /// + public static readonly DependencyProperty IsCollapsedProperty = + DependencyProperty.Register("IsCollapsed", typeof(bool), + typeof(Ribbon), new FrameworkPropertyMetadata(false, OnIsCollapsedChanged)); + + private static void OnIsCollapsedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var ribbon = (Ribbon)d; + if (ribbon.IsCollapsedChanged != null) + { + ribbon.IsCollapsedChanged(ribbon, e); + } + } + + /// + /// Defines if the Ribbon should automatically set when the width or height of the owner window drop under or + /// + public bool IsAutomaticCollapseEnabled + { + get { return (bool)this.GetValue(IsAutomaticCollapseEnabledProperty); } + set { this.SetValue(IsAutomaticCollapseEnabledProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for IsCollapsed. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IsAutomaticCollapseEnabledProperty = + DependencyProperty.Register("IsAutomaticCollapseEnabled", typeof(bool), typeof(Ribbon), new PropertyMetadata(true)); + + /// + /// Gets or sets whether QAT is visible + /// + public bool IsQuickAccessToolBarVisible + { + get { return (bool)this.GetValue(IsQuickAccessToolBarVisibleProperty); } + set { this.SetValue(IsQuickAccessToolBarVisibleProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for IsQuickAccessToolBarVisible. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IsQuickAccessToolBarVisibleProperty = + DependencyProperty.Register("IsQuickAccessToolBarVisible", typeof(bool), typeof(Ribbon), new UIPropertyMetadata(true)); + + + /// + /// Gets or sets whether user can change location of QAT + /// + public bool CanQuickAccessLocationChanging + { + get { return (bool)this.GetValue(CanQuickAccessLocationChangingProperty); } + set { this.SetValue(CanQuickAccessLocationChangingProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for CanQuickAccessLocationChanging. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty CanQuickAccessLocationChangingProperty = + DependencyProperty.Register("CanQuickAccessLocationChanging", typeof(bool), typeof(Ribbon), new UIPropertyMetadata(true)); + + + /// + /// Checks if any keytips are visible. + /// + public bool AreAnyKeyTipsVisible + { + get + { + if (this.keyTipService != null) + { + return this.keyTipService.AreAnyKeyTipsVisible; + } + + return false; + } + } + #endregion + + #region Commands + + /// + /// Gets add to quick access toolbar command + /// + public static RoutedCommand AddToQuickAccessCommand = new RoutedCommand("AddToQuickAccessCommand", typeof(Ribbon)); + + /// + /// Gets remove from quick access command + /// + public static RoutedCommand RemoveFromQuickAccessCommand = new RoutedCommand("RemoveFromQuickAccessCommand", typeof(Ribbon)); + + /// + /// Gets show quick access above command + /// + public static RoutedCommand ShowQuickAccessAboveCommand = new RoutedCommand("ShowQuickAccessAboveCommand", typeof(Ribbon)); + + /// + /// Gets show quick access below command + /// + public static RoutedCommand ShowQuickAccessBelowCommand = new RoutedCommand("ShowQuickAccessBelowCommand", typeof(Ribbon)); + + /// + /// Gets toggle ribbon minimize command + /// + public static RoutedCommand ToggleMinimizeTheRibbonCommand = new RoutedCommand("ToggleMinimizeTheRibbonCommand", typeof(Ribbon)); + + /// + /// Gets customize quick access toolbar command + /// + public static RoutedCommand CustomizeQuickAccessToolbarCommand = new RoutedCommand("CustomizeQuickAccessToolbarCommand", typeof(Ribbon)); + + /// + /// Gets customize the ribbon command + /// + public static RoutedCommand CustomizeTheRibbonCommand = new RoutedCommand("CustomizeTheRibbonCommand", typeof(Ribbon)); + + // Occurs when customize toggle minimize command can execute handles + private static void OnToggleMinimizeTheRibbonCommandCanExecute(object sender, CanExecuteRoutedEventArgs e) + { + var ribbon = sender as Ribbon; + + if (ribbon != null + && ribbon.TabControl != null) + { + e.CanExecute = true; + } + } + + // Occurs when toggle minimize command executed + private static void OnToggleMinimizeTheRibbonCommandExecuted(object sender, ExecutedRoutedEventArgs e) + { + var ribbon = sender as Ribbon; + if (ribbon != null + && ribbon.TabControl != null) + { + ribbon.TabControl.IsMinimized = !ribbon.TabControl.IsMinimized; + } + } + + // Occurs when show quick access below command executed + private static void OnShowQuickAccessBelowCommandExecuted(object sender, ExecutedRoutedEventArgs e) + { + var ribbon = sender as Ribbon; + + if (ribbon == null) + { + return; + } + + ribbon.ShowQuickAccessToolBarAboveRibbon = false; + } + + // Occurs when show quick access above command executed + private static void OnShowQuickAccessAboveCommandExecuted(object sender, ExecutedRoutedEventArgs e) + { + var ribbon = sender as Ribbon; + + if (ribbon == null) + { + return; + } + ribbon.ShowQuickAccessToolBarAboveRibbon = true; + } + + // Occurs when remove from quick access command executed + private static void OnRemoveFromQuickAccessCommandExecuted(object sender, ExecutedRoutedEventArgs e) + { + var ribbon = sender as Ribbon; + + if (ribbon == null) + { + return; + } + + if (ribbon.quickAccessToolBar != null) + { + var element = ribbon.quickAccessElements.First(x => x.Value == e.Parameter).Key; + ribbon.RemoveFromQuickAccessToolBar(element); + } + } + + // Occurs when add to quick access command executed + private static void OnAddToQuickAccessCommandExecuted(object sender, ExecutedRoutedEventArgs e) + { + var ribbon = sender as Ribbon; + + if (ribbon == null) + { + return; + } + + if (ribbon.quickAccessToolBar != null) + { + ribbon.AddToQuickAccessToolBar(e.Parameter as UIElement); + } + } + + // Occurs when customize quick access command executed + private static void OnCustomizeQuickAccessToolbarCommandExecuted(object sender, ExecutedRoutedEventArgs e) + { + var ribbon = sender as Ribbon; + + if (ribbon == null) + { + return; + } + + if (ribbon.CustomizeQuickAccessToolbar != null) + { + ribbon.CustomizeQuickAccessToolbar(sender, EventArgs.Empty); + } + } + + // Occurs when customize the ribbon command executed + private static void OnCustomizeTheRibbonCommandExecuted(object sender, ExecutedRoutedEventArgs e) + { + var ribbon = sender as Ribbon; + + if (ribbon == null) + { + return; + } + + if (ribbon.CustomizeTheRibbon != null) + { + ribbon.CustomizeTheRibbon(sender, EventArgs.Empty); + } + } + + // Occurs when customize quick access command can execute handles + private static void OnCustomizeQuickAccessToolbarCommandCanExecute(object sender, CanExecuteRoutedEventArgs e) + { + var ribbon = sender as Ribbon; + + if (ribbon == null) + { + return; + } + + e.CanExecute = ribbon.CanCustomizeQuickAccessToolBar; + } + + // Occurs when customize the ribbon command can execute handles + private static void OnCustomizeTheRibbonCommandCanExecute(object sender, CanExecuteRoutedEventArgs e) + { + var ribbon = sender as Ribbon; + + if (ribbon == null) + { + return; + } + + e.CanExecute = ribbon.CanCustomizeRibbon; + } + + // Occurs when remove from quick access command can execute handles + private static void OnRemoveFromQuickAccessCommandCanExecute(object sender, CanExecuteRoutedEventArgs e) + { + var ribbon = sender as Ribbon; + + if (ribbon == null) + { + return; + } + + if (ribbon.IsQuickAccessToolBarVisible) + { + e.CanExecute = ribbon.quickAccessElements.ContainsValue(e.Parameter as UIElement); + } + else + { + e.CanExecute = false; + } + } + + // Occurs when add to quick access command can execute handles + private static void OnAddToQuickAccessCommandCanExecute(object sender, CanExecuteRoutedEventArgs e) + { + var ribbon = sender as Ribbon; + + if (ribbon != null + && ribbon.IsQuickAccessToolBarVisible + && QuickAccessItemsProvider.IsSupported(e.Parameter as UIElement) + && ribbon.IsInQuickAccessToolBar(e.Parameter as UIElement) == false) + { + if (e.Parameter is Gallery) + { + e.CanExecute = ribbon.IsInQuickAccessToolBar(FindParentRibbonControl(e.Parameter as DependencyObject) as UIElement) == false; + } + else + { + e.CanExecute = ribbon.IsInQuickAccessToolBar(e.Parameter as UIElement) == false; + } + } + else + { + e.CanExecute = false; + } + } + + #endregion + + #region Constructors + + /// + /// Static constructor + /// + [SuppressMessage("Microsoft.Performance", "CA1810")] + static Ribbon() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(Ribbon), new FrameworkPropertyMetadata(typeof(Ribbon))); + + // Subscribe to menu commands + CommandManager.RegisterClassCommandBinding(typeof(Ribbon), new CommandBinding(AddToQuickAccessCommand, OnAddToQuickAccessCommandExecuted, OnAddToQuickAccessCommandCanExecute)); + CommandManager.RegisterClassCommandBinding(typeof(Ribbon), new CommandBinding(RemoveFromQuickAccessCommand, OnRemoveFromQuickAccessCommandExecuted, OnRemoveFromQuickAccessCommandCanExecute)); + CommandManager.RegisterClassCommandBinding(typeof(Ribbon), new CommandBinding(ShowQuickAccessAboveCommand, OnShowQuickAccessAboveCommandExecuted)); + CommandManager.RegisterClassCommandBinding(typeof(Ribbon), new CommandBinding(ShowQuickAccessBelowCommand, OnShowQuickAccessBelowCommandExecuted)); + CommandManager.RegisterClassCommandBinding(typeof(Ribbon), new CommandBinding(ToggleMinimizeTheRibbonCommand, OnToggleMinimizeTheRibbonCommandExecuted, OnToggleMinimizeTheRibbonCommandCanExecute)); + CommandManager.RegisterClassCommandBinding(typeof(Ribbon), new CommandBinding(CustomizeTheRibbonCommand, OnCustomizeTheRibbonCommandExecuted, OnCustomizeTheRibbonCommandCanExecute)); + CommandManager.RegisterClassCommandBinding(typeof(Ribbon), new CommandBinding(CustomizeQuickAccessToolbarCommand, OnCustomizeQuickAccessToolbarCommandExecuted, OnCustomizeQuickAccessToolbarCommandCanExecute)); + + InitRibbonContextMenu(); + } + + /// + /// Default constructor + /// + public Ribbon() + { + this.VerticalAlignment = VerticalAlignment.Top; + KeyboardNavigation.SetDirectionalNavigation(this, KeyboardNavigationMode.Contained); + + this.keyTipService = new KeyTipService(this); + + this.Loaded += this.OnLoaded; + this.Unloaded += this.OnUnloaded; + + this.ribbonState = new RibbonState(this); + } + + #endregion + + #region Overrides + + private void OnSizeChanged(object sender, SizeChangedEventArgs e) + { + this.MaintainIsCollapsed(); + } + + private void MaintainIsCollapsed() + { + if (this.IsAutomaticCollapseEnabled == false + || this.ownerWindow == null) + { + return; + } + + if (this.ownerWindow.ActualWidth < MinimalVisibleWidth + || this.ownerWindow.ActualHeight < MinimalVisibleHeight) + { + this.IsCollapsed = true; + } + else + { + this.IsCollapsed = false; + } + } + + /// + /// Invoked whenever an unhandled System.Windows.UIElement.GotFocus + /// event reaches this element in its route. + /// + /// The System.Windows.RoutedEventArgs that contains the event data. + protected override void OnGotFocus(RoutedEventArgs e) + { + if (this.TabControl == null) + { + return; + } + + var ribbonTabItem = (RibbonTabItem)this.TabControl.SelectedItem; + if (ribbonTabItem != null) + { + ribbonTabItem.Focus(); + } + } + + /// + /// When overridden in a derived class, is invoked whenever application code or + /// internal processes call System.Windows.FrameworkElement.ApplyTemplate(). + /// + [SuppressMessage("Microsoft.Maintainability", "CA1502")] + public override void OnApplyTemplate() + { + if (this.layoutRoot != null) + { + this.RemoveLogicalChild(this.layoutRoot); + } + + this.layoutRoot = this.GetTemplateChild("PART_LayoutRoot") as Panel; + + if (this.layoutRoot != null) + { + this.AddLogicalChild(this.layoutRoot); + } + + if (this.TitleBar != null) + { + foreach (var ribbonContextualTabGroup in this.ContextualGroups) + { + this.TitleBar.Items.Remove(ribbonContextualTabGroup); + } + + // Make sure everything is cleared + this.TitleBar.Items.Clear(); + } + + this.TitleBar = this.GetTemplateChild("PART_RibbonTitleBar") as RibbonTitleBar; + + if (this.TitleBar != null) + { + foreach (var contextualTabGroup in this.ContextualGroups) + { + this.TitleBar.Items.Add(contextualTabGroup); + } + } + + var selectedTab = this.SelectedTabItem; + if (this.TabControl != null) + { + this.TabControl.SelectionChanged -= this.OnTabControlSelectionChanged; + selectedTab = this.TabControl.SelectedItem as RibbonTabItem; + + foreach (var ribbonTabItem in this.Tabs) + { + this.TabControl.Items.Remove(ribbonTabItem); + } + + // Make sure everything is cleared + this.TabControl.Items.Clear(); + + foreach (var toolBarItem in this.ToolBarItems) + { + this.TabControl.ToolBarItems.Remove(toolBarItem); + } + + // Make sure everything is cleared + this.TabControl.ToolBarItems.Clear(); + } + + this.TabControl = this.GetTemplateChild("PART_RibbonTabControl") as RibbonTabControl; + + if (this.TabControl != null) + { + this.TabControl.SelectionChanged += this.OnTabControlSelectionChanged; + + this.TabControl.IsMinimized = this.IsMinimized; + this.TabControl.ContentGapHeight = this.ContentGapHeight; + + this.TabControl.SetBinding(RibbonTabControl.IsMinimizedProperty, new Binding("IsMinimized") { Source = this, Mode = BindingMode.TwoWay }); + this.TabControl.SetBinding(RibbonTabControl.ContentGapHeightProperty, new Binding("ContentGapHeight") { Source = this, Mode = BindingMode.OneWay }); + + foreach (var ribbonTabItem in this.Tabs) + { + this.TabControl.Items.Add(ribbonTabItem); + } + + this.TabControl.SelectedItem = selectedTab; + + foreach (var toolBarItem in this.ToolBarItems) + { + this.TabControl.ToolBarItems.Add(toolBarItem); + } + } + + if (this.quickAccessToolBar != null) + { + if (this.AutomaticStateManagement == false + || this.ribbonState.IsStateLoaded) + { + this.ribbonState.SaveStateToMemoryStream(); + } + + this.ClearQuickAccessToolBar(); + + this.quickAccessToolBar.ItemsChanged -= this.OnQuickAccessItemsChanged; + + foreach (var quickAccessMenuItem in this.QuickAccessItems) + { + this.quickAccessToolBar.QuickAccessItems.Remove(quickAccessMenuItem); + } + } + + this.quickAccessToolBar = this.GetTemplateChild("PART_QuickAccessToolBar") as QuickAccessToolBar; + + if (this.quickAccessToolBar != null) + { + foreach (var quickAccessMenuItem in this.QuickAccessItems) + { + this.quickAccessToolBar.QuickAccessItems.Add(quickAccessMenuItem); + } + + this.quickAccessToolBar.ItemsChanged += this.OnQuickAccessItemsChanged; + + var binding = new Binding("CanQuickAccessLocationChanging") + { + Source = this, + Mode = BindingMode.OneWay + }; + this.quickAccessToolBar.SetBinding(QuickAccessToolBar.CanQuickAccessLocationChangingProperty, binding); + + if (this.quickAccessToolBar.Parent == null) + { + this.AddLogicalChild(this.quickAccessToolBar); + } + + this.quickAccessToolBar.Loaded += this.OnFirstToolbarLoaded; + } + } + + /// + /// Called when the is closed, so that we set it to null and clear the + /// + private void OnOwnerWindowClosed(object sender, EventArgs e) + { + this.DetachFromWindow(); + } + + private void AttachToWindow() + { + this.DetachFromWindow(); + + this.ownerWindow = Window.GetWindow(this); + + if (this.ownerWindow != null) + { + this.ownerWindow.Closed += this.OnOwnerWindowClosed; + this.ownerWindow.SizeChanged += this.OnSizeChanged; + this.ownerWindow.KeyDown += this.OnKeyDown; + + var binding = new Binding("Title") + { + Mode = BindingMode.OneWay, + Source = this.ownerWindow + }; + this.SetBinding(TitleProperty, binding); + } + } + + private void DetachFromWindow() + { + if (this.ownerWindow != null) + { + this.ribbonState.SaveStateToIsolatedStorage(); + + this.ownerWindow.Closed -= this.OnOwnerWindowClosed; + this.ownerWindow.SizeChanged -= this.OnSizeChanged; + this.ownerWindow.KeyDown -= this.OnKeyDown; + + BindingOperations.ClearBinding(this, TitleProperty); + } + + this.ownerWindow = null; + } + + private void OnFirstToolbarLoaded(object sender, RoutedEventArgs e) + { + this.quickAccessToolBar.Loaded -= this.OnFirstToolbarLoaded; + + this.ribbonState.LoadStateFromMemoryStream(); + } + + #endregion + + #region Quick Access Items Managment + + /// + /// Determines whether the given element is in quick access toolbar + /// + /// Element + /// True if element in quick access toolbar + public bool IsInQuickAccessToolBar(UIElement element) + { + if (element == null) + { + return false; + } + + return this.quickAccessElements.ContainsKey(element); + } + + /// + /// Adds the given element to quick access toolbar + /// + /// Element + public void AddToQuickAccessToolBar(UIElement element) + { + if (element == null) + { + return; + } + + if (element is Gallery) + { + element = FindParentRibbonControl(element) as UIElement; + } + + // Do not add menu items without icon. + var menuItem = element as MenuItem; + if (menuItem != null && menuItem.Icon == null) + { + element = FindParentRibbonControl(element) as UIElement; + } + + if (element == null) + { + return; + } + + if (QuickAccessItemsProvider.IsSupported(element) == false) + { + return; + } + + if (this.IsInQuickAccessToolBar(element) == false) + { + Debug.WriteLine("Adding \"{0}\" to QuickAccessToolBar.", element); + + var control = QuickAccessItemsProvider.GetQuickAccessItem(element); + + this.quickAccessElements.Add(element, control); + this.quickAccessToolBar.Items.Add(control); + } + } + + private static IRibbonControl FindParentRibbonControl(DependencyObject element) + { + var parent = LogicalTreeHelper.GetParent(element); + + while (parent != null) + { + var control = parent as IRibbonControl; + if (control != null) + { + return control; + } + + parent = LogicalTreeHelper.GetParent(parent); + } + + return null; + } + + /// + /// Removes the given elements from quick access toolbar + /// + /// Element + public void RemoveFromQuickAccessToolBar(UIElement element) + { + Debug.WriteLine("Removing \"{0}\" from QuickAccessToolBar.", element); + + if (this.IsInQuickAccessToolBar(element)) + { + var quickAccessItem = this.quickAccessElements[element]; + this.quickAccessElements.Remove(element); + this.quickAccessToolBar.Items.Remove(quickAccessItem); + } + } + + /// + /// Clears quick access toolbar + /// + public void ClearQuickAccessToolBar() + { + this.quickAccessElements.Clear(); + if (this.quickAccessToolBar != null) + { + this.quickAccessToolBar.Items.Clear(); + } + } + + #endregion + + #region Event Handling + + // Handles tab control selection changed + private void OnTabControlSelectionChanged(object sender, SelectionChangedEventArgs e) + { + if (this.TabControl != null) + { + this.SelectedTabItem = this.TabControl.SelectedItem as RibbonTabItem; + this.SelectedTabIndex = this.TabControl.SelectedIndex; + } + + if (this.SelectedTabChanged != null) + { + this.SelectedTabChanged(this, e); + } + } + + private void OnLoaded(object sender, RoutedEventArgs e) + { + this.keyTipService.Attach(); + + this.AttachToWindow(); + + this.LoadInitialState(); + } + + private void OnKeyDown(object sender, KeyEventArgs e) + { + if (e.Key == Key.F1 + && Keyboard.Modifiers.HasFlag(ModifierKeys.Control)) + { + if (this.TabControl.HasItems) + { + this.IsMinimized = !this.IsMinimized; + } + } + } + + private void OnUnloaded(object sender, RoutedEventArgs e) + { + this.ribbonState.SaveStateToIsolatedStorage(); + + this.keyTipService.Detach(); + + if (this.ownerWindow != null) + { + this.ownerWindow.SizeChanged -= this.OnSizeChanged; + this.ownerWindow.KeyDown -= this.OnKeyDown; + } + } + + #endregion + + #region Private methods + + private RibbonTabItem GetFirstVisibleItem() + { + return this.Tabs.FirstOrDefault(item => item.Visibility == Visibility.Visible); + } + + private RibbonTabItem GetLastVisibleItem() + { + return this.Tabs.LastOrDefault(item => item.Visibility == Visibility.Visible); + } + + #endregion + + #region State Management + + private void LoadInitialState() + { + if (this.ribbonState.IsStateLoaded) + { + return; + } + + this.ribbonState.LoadStateFromIsolatedStorage(); + + if (this.TabControl != null + && this.TabControl.SelectedIndex == -1 + && this.TabControl.IsMinimized == false) + { + this.TabControl.SelectedIndex = 0; + } + } + + // Handles items changing in QAT + private void OnQuickAccessItemsChanged(object sender, NotifyCollectionChangedEventArgs e) + { + this.ribbonState.SaveStateToMemoryStream(); + } + + // Traverse logical tree and find QAT items, remember paths + private void TraverseLogicalTree(DependencyObject item, string path, IDictionary paths) + { + // Is this item in QAT + var uielement = item as FrameworkElement; + if (uielement != null + && this.quickAccessElements.ContainsKey(uielement)) + { + if (!paths.ContainsKey(uielement)) + { + paths.Add(uielement, path); + } + } + + var children = LogicalTreeHelper.GetChildren(item).Cast().ToArray(); + for (var i = 0; i < children.Length; i++) + { + var child = children[i] as DependencyObject; + if (child == null) + { + continue; + } + + this.TraverseLogicalTree(child, path + i + ",", paths); + } + } + + #endregion + + #region Load from Stream + + // Loads item and add to QAT + private void ParseAndAddToQuickAccessToolBar(string data) + { + var indices = data.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) + .Select(x => int.Parse(x, CultureInfo.InvariantCulture)).ToArray(); + + DependencyObject current = this; + + for (var i = 0; i < indices.Length; i++) + { + var children = LogicalTreeHelper.GetChildren(current).OfType().ToArray(); + var indexIsInvalid = children.Length <= indices[i]; + var item = indexIsInvalid + ? null + : children[indices[i]] as DependencyObject; + + if (item == null) + { + // Path is incorrect + Debug.WriteLine("Error while QAT items loading: one of the paths is invalid"); + return; + } + + current = item; + } + + var result = current as UIElement; + if (result == null + || QuickAccessItemsProvider.IsSupported(result) == false) + { + // Item is invalid + Debug.WriteLine("Error while QAT items loading. Could not add \"{0}\" to QAT.", current); + return; + } + + this.AddToQuickAccessToolBar(result); + } + + #endregion + + #region AutomaticStateManagement Property + + // To temporary suppress automatic management + private bool suppressAutomaticStateManagement; + + /// + /// Gets or sets whether Quick Access ToolBar can + /// save and load its state automatically + /// + public bool AutomaticStateManagement + { + get { return (bool)this.GetValue(AutomaticStateManagementProperty); } + set { this.SetValue(AutomaticStateManagementProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for AutomaticStateManagement. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty AutomaticStateManagementProperty = + DependencyProperty.Register("AutomaticStateManagement", typeof(bool), typeof(Ribbon), new UIPropertyMetadata(true, OnAutoStateManagement, CoerceAutoStateManagement)); + + private static object CoerceAutoStateManagement(DependencyObject d, object basevalue) + { + var ribbon = (Ribbon)d; + if (ribbon.suppressAutomaticStateManagement) + { + return false; + } + + return basevalue; + } + + private static void OnAutoStateManagement(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var ribbon = (Ribbon)d; + if ((bool)e.NewValue) + { + ribbon.LoadInitialState(); + } + } + + #endregion + + private class RibbonState : IDisposable + { + private readonly Ribbon ribbon; + + // Name of the isolated storage file + private string isolatedStorageFileName; + private Stream memoryStream; + + public RibbonState(Ribbon ribbon) + { + this.ribbon = ribbon; + this.memoryStream = new MemoryStream(); + } + + /// + /// Gets or sets whether state is loaded + /// + public bool IsStateLoaded { get; private set; } + + /// + /// Gets name of the isolated storage file + /// + private string IsolatedStorageFileName + { + get + { + if (this.isolatedStorageFileName != null) + { + return this.isolatedStorageFileName; + } + + var stringForHash = ""; + var window = Window.GetWindow(this.ribbon); + + if (window != null) + { + stringForHash += "." + window.GetType().FullName; + + if (string.IsNullOrEmpty(window.Name) == false + && window.Name.Trim().Length > 0) + { + stringForHash += "." + window.Name; + } + } + + if (string.IsNullOrEmpty(this.ribbon.Name) == false + && this.ribbon.Name.Trim().Length > 0) + { + stringForHash += "." + this.ribbon.Name; + } + + this.isolatedStorageFileName = "Fluent.Ribbon.State.2.0." + stringForHash.GetHashCode().ToString("X"); + return this.isolatedStorageFileName; + } + } + + public void SaveStateToMemoryStream() + { + this.memoryStream.Position = 0; + this.SaveState(this.memoryStream); + } + + // Saves to Isolated Storage (in user store for domain) + public void SaveStateToIsolatedStorage() + { + // Check whether automatic save is valid now + if (this.ribbon.AutomaticStateManagement == false) + { + Trace.WriteLine(string.Format("State not saved to isolated storage. Because automatic state management is disabled.")); + return; + } + + if (this.IsStateLoaded == false) + { + Trace.WriteLine(string.Format("State not saved to isolated storage. Because state was not loaded before.")); + return; + } + + try + { + var storage = GetIsolatedStorageFile(); + + using (var stream = new IsolatedStorageFileStream(this.IsolatedStorageFileName, FileMode.Create, FileAccess.Write, storage)) + { + this.SaveState(stream); + } + } + catch (Exception ex) + { + Trace.WriteLine(string.Format("Error while trying to save Ribbon state. Error: {0}", ex)); + } + } + + /// + /// Saves state to the given stream + /// + /// Stream + private void SaveState(Stream stream) + { + // Don't save or load state in design mode + if (DesignerProperties.GetIsInDesignMode(this.ribbon)) + { + return; + } + + var builder = new StringBuilder(); + + var isMinimizedSaveState = this.ribbon.IsMinimized; + + // Save Ribbon State + builder.Append(isMinimizedSaveState.ToString(CultureInfo.InvariantCulture)); + builder.Append(','); + builder.Append(this.ribbon.ShowQuickAccessToolBarAboveRibbon.ToString(CultureInfo.InvariantCulture)); + builder.Append('|'); + + // Save QAT items + var paths = new Dictionary(); + this.ribbon.TraverseLogicalTree(this.ribbon, "", paths); + + // Foreach items and see whether path is found for the item + foreach (var element in this.ribbon.quickAccessElements) + { + string path; + var control = element.Key as FrameworkElement; + + if (control != null + && paths.TryGetValue(control, out path)) + { + builder.Append(path); + builder.Append(';'); + } + else + { + // Item is not found in logical tree, output to debug console + var controlName = (control != null && string.IsNullOrEmpty(control.Name) == false) + ? string.Format(CultureInfo.InvariantCulture, " (name of the control is {0})", control.Name) + : string.Empty; + + Debug.WriteLine("Control " + element.Key.GetType().Name + " is not found in logical tree during QAT saving" + controlName); + } + } + + var writer = new StreamWriter(stream); + writer.Write(builder.ToString()); + + writer.Flush(); + } + + public void LoadStateFromMemoryStream() + { + this.memoryStream.Position = 0; + this.LoadState(this.memoryStream); + } + + /// + /// Loads the State from Isolated Storage (in user store for domain) + /// + /// + /// Sets after it's finished to prevent a race condition with saving the state to the MemoryStream. + /// + public void LoadStateFromIsolatedStorage() + { + // Don't save or load state in design mode + if (DesignerProperties.GetIsInDesignMode(this.ribbon)) + { + Trace.WriteLine(string.Format("State not loaded from isolated storage. Because we are in design mode.")); + this.IsStateLoaded = true; + return; + } + + if (this.ribbon.AutomaticStateManagement == false) + { + this.IsStateLoaded = true; + Trace.WriteLine(string.Format("State not loaded from isolated storage. Because automatic state management is disabled.")); + return; + } + + try + { + var storage = GetIsolatedStorageFile(); + if (FileExists(storage, this.IsolatedStorageFileName)) + { + using (var stream = new IsolatedStorageFileStream(this.IsolatedStorageFileName, FileMode.Open, FileAccess.Read, storage)) + { + this.LoadState(stream); + + // Copy loaded state to MemoryStream for temporary storage. + // Temporary storage is used for style changes etc. so we can apply the current state again. + stream.Position = 0; + this.memoryStream.Position = 0; + stream.CopyTo(this.memoryStream); + } + } + } + catch (Exception ex) + { + Trace.WriteLine(string.Format("Error while trying to load Ribbon state. Error: {0}", ex)); + } + + this.IsStateLoaded = true; + } + + /// + /// Loads state from the given stream + /// + /// Stream + private void LoadState(Stream stream) + { + this.ribbon.suppressAutomaticStateManagement = true; + + var reader = new StreamReader(stream); + var splitted = reader.ReadToEnd().Split('|'); + + if (splitted.Length != 2) + { + return; + } + + // Load Ribbon State + var ribbonProperties = splitted[0].Split(','); + + var isMinimized = bool.Parse(ribbonProperties[0]); + + this.ribbon.IsMinimized = isMinimized; + + this.ribbon.ShowQuickAccessToolBarAboveRibbon = bool.Parse(ribbonProperties[1]); + + // Load items + var items = splitted[1].Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); + + if (this.ribbon.quickAccessToolBar != null) + { + this.ribbon.quickAccessToolBar.Items.Clear(); + } + + this.ribbon.quickAccessElements.Clear(); + + for (var i = 0; i < items.Length; i++) + { + this.ribbon.ParseAndAddToQuickAccessToolBar(items[i]); + } + + // Since application is not fully loaded we have to delay the refresh + this.ribbon.RunInDispatcherAsync(this.ribbon.QuickAccessToolBar.Refresh, DispatcherPriority.Background); + + // Sync QAT menu items + foreach (var menuItem in this.ribbon.QuickAccessItems) + { + menuItem.IsChecked = this.ribbon.IsInQuickAccessToolBar(menuItem.Target); + } + + this.ribbon.suppressAutomaticStateManagement = false; + } + + // Determinates whether the given file exists in the given storage + private static bool FileExists(IsolatedStorageFile storage, string fileName) + { + var files = storage.GetFileNames(fileName); + return files.Length != 0; + } + + // Gets a proper isolated storage file + private static IsolatedStorageFile GetIsolatedStorageFile() + { + try + { + return IsolatedStorageFile.GetUserStoreForDomain(); + } + catch + { + return IsolatedStorageFile.GetUserStoreForAssembly(); + } + } + + /// + /// Resets automatically saved state + /// + public static void ResetState() + { + var storage = GetIsolatedStorageFile(); + + foreach (var filename in storage.GetFileNames("*Fluent.Ribbon.State*")) + { + storage.DeleteFile(filename); + } + } + + private void Dispose(bool disposing) + { + if (disposing == false) + { + return; + } + + if (this.memoryStream != null) + { + this.memoryStream.Dispose(); + } + + this.memoryStream = Stream.Null; + } + + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + } + } } \ No newline at end of file diff --git a/Fluent/Controls/RibbonContextualGroupsContainer.cs b/Fluent.Ribbon/Controls/RibbonContextualGroupsContainer.cs similarity index 97% rename from Fluent/Controls/RibbonContextualGroupsContainer.cs rename to Fluent.Ribbon/Controls/RibbonContextualGroupsContainer.cs index 218fe7c55..e3c9d155a 100644 --- a/Fluent/Controls/RibbonContextualGroupsContainer.cs +++ b/Fluent.Ribbon/Controls/RibbonContextualGroupsContainer.cs @@ -1,139 +1,139 @@ -namespace Fluent -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Windows; - using System.Windows.Controls; - using System.Windows.Media; - - /// - /// Represents contextual groups container - /// - public class RibbonContextualGroupsContainer : Panel - { - private readonly List sizes = new List(); - - /// - /// When overridden in a derived class, positions child elements and determines a size for - /// a System.Windows.FrameworkElement derived class. - /// - /// The final area within the parent that this element should - /// use to arrange itself and its children. - /// The actual size used. - protected override Size ArrangeOverride(Size finalSize) - { - var finalRect = new Rect(finalSize); - var index = 0; - - foreach (UIElement item in this.InternalChildren) - { - finalRect.Width = this.sizes[index].Width;//item.DesiredSize.Width; - finalRect.Height = Math.Max(finalSize.Height, this.sizes[index].Height);//Math.Max(finalSize.Height, item.DesiredSize.Height); - item.Arrange(finalRect); - finalRect.X += this.sizes[index].Width;// item.DesiredSize.Width; - index++; - } - return finalSize; - } - - /// - /// When overridden in a derived class, measures the size in layout required for - /// child elements and determines a size for the System.Windows.FrameworkElement-derived class. - /// - /// The available size that this element can give to child elements. - /// Infinity can be specified as a value to indicate that the element will size to whatever content is available. - /// The size that this element determines it needs during layout, based on its calculations of child element sizes. - protected override Size MeasureOverride(Size availableSize) - { - var x = 0D; - this.sizes.Clear(); - var infinity = new Size(double.PositiveInfinity, double.PositiveInfinity); - - foreach (RibbonContextualTabGroup contextualGroup in this.InternalChildren) - { - // Calculate width of tab items of the group - var tabsWidth = 0D; - - var visibleItems = contextualGroup.Items.Where(group => group.Visibility == Visibility.Visible).ToList(); - - foreach (var item in visibleItems) - { - tabsWidth += item.DesiredSize.Width; - } - - contextualGroup.Measure(infinity); - var groupWidth = contextualGroup.DesiredSize.Width; - - var tabWasChanged = false; - - if (groupWidth > tabsWidth) - { - // If tab's width is less than group's width we have to stretch tabs - var delta = (groupWidth - tabsWidth) / visibleItems.Count; - - foreach (var item in visibleItems) - { - if (item.DesiredWidth == 0) - { - item.DesiredWidth = item.DesiredSize.Width + delta; - item.Measure(new Size(item.DesiredWidth, item.DesiredSize.Height)); - tabWasChanged = true; - } - } - } - - if (tabWasChanged) - { - // If we have changed tabs layout we have - // to invalidate down to RibbonTabsContainer - var visual = visibleItems[0] as Visual; - while (visual != null) - { - var uiElement = visual as UIElement; - if (uiElement != null) - { - if (uiElement is RibbonTabsContainer) - { - uiElement.InvalidateMeasure(); - break; - } - - uiElement.InvalidateMeasure(); - } - - visual = VisualTreeHelper.GetParent(visual) as Visual; - } - - tabsWidth = 0; - - foreach (var item in visibleItems) - { - tabsWidth += item.DesiredSize.Width; - } - } - - // Calc final width and measure the group using it - var finalWidth = tabsWidth; - x += finalWidth; - - if (x > availableSize.Width) - { - finalWidth -= x - availableSize.Width; - x = availableSize.Width; - } - - contextualGroup.Measure(new Size(Math.Max(0, finalWidth), availableSize.Height)); - this.sizes.Add(new Size(Math.Max(0, finalWidth), availableSize.Height)); - } - - var height = availableSize.Height; - if (double.IsPositiveInfinity(height)) - { - height = 0; - } - - return new Size(x, height); - } - } +namespace Fluent +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Windows; + using System.Windows.Controls; + using System.Windows.Media; + + /// + /// Represents contextual groups container + /// + public class RibbonContextualGroupsContainer : Panel + { + private readonly List sizes = new List(); + + /// + /// When overridden in a derived class, positions child elements and determines a size for + /// a System.Windows.FrameworkElement derived class. + /// + /// The final area within the parent that this element should + /// use to arrange itself and its children. + /// The actual size used. + protected override Size ArrangeOverride(Size finalSize) + { + var finalRect = new Rect(finalSize); + var index = 0; + + foreach (UIElement item in this.InternalChildren) + { + finalRect.Width = this.sizes[index].Width;//item.DesiredSize.Width; + finalRect.Height = Math.Max(finalSize.Height, this.sizes[index].Height);//Math.Max(finalSize.Height, item.DesiredSize.Height); + item.Arrange(finalRect); + finalRect.X += this.sizes[index].Width;// item.DesiredSize.Width; + index++; + } + return finalSize; + } + + /// + /// When overridden in a derived class, measures the size in layout required for + /// child elements and determines a size for the System.Windows.FrameworkElement-derived class. + /// + /// The available size that this element can give to child elements. + /// Infinity can be specified as a value to indicate that the element will size to whatever content is available. + /// The size that this element determines it needs during layout, based on its calculations of child element sizes. + protected override Size MeasureOverride(Size availableSize) + { + var x = 0D; + this.sizes.Clear(); + var infinity = new Size(double.PositiveInfinity, double.PositiveInfinity); + + foreach (RibbonContextualTabGroup contextualGroup in this.InternalChildren) + { + // Calculate width of tab items of the group + var tabsWidth = 0D; + + var visibleItems = contextualGroup.Items.Where(group => group.Visibility == Visibility.Visible).ToList(); + + foreach (var item in visibleItems) + { + tabsWidth += item.DesiredSize.Width; + } + + contextualGroup.Measure(infinity); + var groupWidth = contextualGroup.DesiredSize.Width; + + var tabWasChanged = false; + + if (groupWidth > tabsWidth) + { + // If tab's width is less than group's width we have to stretch tabs + var delta = (groupWidth - tabsWidth) / visibleItems.Count; + + foreach (var item in visibleItems) + { + if (item.DesiredWidth == 0) + { + item.DesiredWidth = item.DesiredSize.Width + delta; + item.Measure(new Size(item.DesiredWidth, item.DesiredSize.Height)); + tabWasChanged = true; + } + } + } + + if (tabWasChanged) + { + // If we have changed tabs layout we have + // to invalidate down to RibbonTabsContainer + var visual = visibleItems[0] as Visual; + while (visual != null) + { + var uiElement = visual as UIElement; + if (uiElement != null) + { + if (uiElement is RibbonTabsContainer) + { + uiElement.InvalidateMeasure(); + break; + } + + uiElement.InvalidateMeasure(); + } + + visual = VisualTreeHelper.GetParent(visual) as Visual; + } + + tabsWidth = 0; + + foreach (var item in visibleItems) + { + tabsWidth += item.DesiredSize.Width; + } + } + + // Calc final width and measure the group using it + var finalWidth = tabsWidth; + x += finalWidth; + + if (x > availableSize.Width) + { + finalWidth -= x - availableSize.Width; + x = availableSize.Width; + } + + contextualGroup.Measure(new Size(Math.Max(0, finalWidth), availableSize.Height)); + this.sizes.Add(new Size(Math.Max(0, finalWidth), availableSize.Height)); + } + + var height = availableSize.Height; + if (double.IsPositiveInfinity(height)) + { + height = 0; + } + + return new Size(x, height); + } + } } \ No newline at end of file diff --git a/Fluent/Controls/RibbonContextualTabGroup.cs b/Fluent.Ribbon/Controls/RibbonContextualTabGroup.cs similarity index 97% rename from Fluent/Controls/RibbonContextualTabGroup.cs rename to Fluent.Ribbon/Controls/RibbonContextualTabGroup.cs index 682923d22..ba3e33788 100644 --- a/Fluent/Controls/RibbonContextualTabGroup.cs +++ b/Fluent.Ribbon/Controls/RibbonContextualTabGroup.cs @@ -1,366 +1,366 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Input; - -namespace Fluent -{ - /// - /// Represents contextual tab group - /// - public class RibbonContextualTabGroup : Control - { - #region Fields - - // Collection of ribbon tab items - private readonly List items = new List(); - - private Window parentWidow; - - #endregion - - #region Properties - - /// - /// Gets or sets group header - /// - public string Header - { - get { return (string)this.GetValue(HeaderProperty); } - set { this.SetValue(HeaderProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Header. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty HeaderProperty = - DependencyProperty.Register("Header", typeof(string), typeof(RibbonContextualTabGroup), - new UIPropertyMetadata("RibbonContextualTabGroup", OnHeaderChanged)); - - /// - /// Handles header chages - /// - /// Object - /// The event data. - private static void OnHeaderChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - } - - /// - /// Gets collection of tab items - /// - public List Items - { - get { return this.items; } - } - - /// - /// Gets or sets a value indicating whether parent window is maximized - /// - public bool IsWindowMaximized - { - get { return (bool)this.GetValue(IsWindowMaximizedProperty); } - set { this.SetValue(IsWindowMaximizedProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for IsWindowMaximized. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IsWindowMaximizedProperty = - DependencyProperty.Register("IsWindowMaximized", typeof(bool), typeof(RibbonContextualTabGroup), new UIPropertyMetadata(false)); - - /// - /// Gets or sets the visibility this group for internal use (this enables us to hide this group when all items in this group are hidden) - /// - public Visibility InnerVisibility - { - get { return (Visibility)this.GetValue(InnerVisibilityProperty); } - private set { this.SetValue(InnerVisibilityPropertyKey, value); } - } - - private static readonly DependencyPropertyKey InnerVisibilityPropertyKey = - DependencyProperty.RegisterReadOnly("InnerVisibility", typeof(Visibility), typeof(RibbonContextualTabGroup), new UIPropertyMetadata(Visibility.Visible)); - - /// - /// Using a DependencyProperty as the backing store for InnerVisibility. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty InnerVisibilityProperty = InnerVisibilityPropertyKey.DependencyProperty; - - /// - /// Gets the first visible TabItem in this group - /// - public RibbonTabItem FirstVisibleItem - { - get - { - return this.GetFirstVisibleItem(); - } - } - - /// - /// Gets the last visible TabItem in this group - /// - public RibbonTabItem LastVisibleItem - { - get - { - return this.GetLastVisibleItem(); - } - } - - #endregion - - #region Initialization - - /// - /// Static constructor - /// - [SuppressMessage("Microsoft.Performance", "CA1810")] - static RibbonContextualTabGroup() - { - DefaultStyleKeyProperty.OverrideMetadata(typeof(RibbonContextualTabGroup), new FrameworkPropertyMetadata(typeof(RibbonContextualTabGroup))); - VisibilityProperty.OverrideMetadata(typeof(RibbonContextualTabGroup), new PropertyMetadata(Visibility.Collapsed, OnVisibilityChanged)); - StyleProperty.OverrideMetadata(typeof(RibbonContextualTabGroup), new FrameworkPropertyMetadata(null, OnCoerceStyle)); - } - - // Coerce object style - static object OnCoerceStyle(DependencyObject d, object basevalue) - { - if (basevalue == null) - { - basevalue = ((FrameworkElement)d).TryFindResource(typeof(RibbonContextualTabGroup)); - } - - return basevalue; - } - - /// - /// Handles visibility prioperty changed - /// - /// Object - /// The event data - private static void OnVisibilityChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var group = (RibbonContextualTabGroup)d; - - foreach (var tab in group.Items) - { - tab.Visibility = group.Visibility; - } - - group.UpdateInnerVisiblityAndGroupBorders(); - - var titleBar = group.Parent as RibbonTitleBar; - - if (titleBar != null) - { - titleBar.InvalidateMeasure(); - } - } - - /// - /// Default constructor - /// - public RibbonContextualTabGroup() - { - this.Loaded += this.OnLoaded; - this.Unloaded += this.OnUnloaded; - } - - private void OnLoaded(object sender, RoutedEventArgs e) - { - this.parentWidow = Window.GetWindow(this); - - this.SubscribeEvents(); - this.UpdateInnerVisibility(); - - if (this.parentWidow != null) - { - this.IsWindowMaximized = this.parentWidow.WindowState == WindowState.Maximized; - } - } - - private void OnUnloaded(object sender, RoutedEventArgs e) - { - this.UnSubscribeEvents(); - - this.parentWidow = null; - } - - private void SubscribeEvents() - { - // Always unsubscribe events to ensure we don't subscribe twice - this.UnSubscribeEvents(); - - if (this.parentWidow != null) - { - this.parentWidow.StateChanged += this.OnParentWindowStateChanged; - } - } - - private void UnSubscribeEvents() - { - if (this.parentWidow != null) - { - this.parentWidow.StateChanged -= this.OnParentWindowStateChanged; - } - } - - #endregion - - #region Internal Methods - - /// - /// Appends tab item - /// - /// Ribbon tab item - internal void AppendTabItem(RibbonTabItem item) - { - this.Items.Add(item); - this.UpdateInnerVisiblityAndGroupBorders(); - } - - /// - /// Removes tab item - /// - /// Ribbon tab item - internal void RemoveTabItem(RibbonTabItem item) - { - this.Items.Remove(item); - this.UpdateInnerVisiblityAndGroupBorders(); - } - - private RibbonTabItem GetFirstVisibleItem() - { - return this.items.FirstOrDefault(item => item.Visibility == Visibility.Visible); - } - - private RibbonTabItem GetLastVisibleItem() - { - return this.items.LastOrDefault(item => item.Visibility == Visibility.Visible); - } - - /// - /// Updates the group border - /// - public void UpdateInnerVisiblityAndGroupBorders() - { - this.UpdateInnerVisibility(); - - var leftset = false; - var rightset = false; - - for (var i = 0; i < this.items.Count; i++) - { - //if (i == 0) items[i].HasLeftGroupBorder = true; - //else items[i].HasLeftGroupBorder = false; - //if (i == items.Count - 1) items[i].HasRightGroupBorder = true; - //else items[i].HasRightGroupBorder = false; - - //Workaround so you can have inivisible Tabs on a Group - if (this.items[i].Visibility == Visibility.Visible - && leftset == false) - { - this.items[i].HasLeftGroupBorder = true; - leftset = true; - } - else - { - this.items[i].HasLeftGroupBorder = false; - } - - if (this.items[this.items.Count - 1 - i].Visibility == Visibility.Visible - && rightset == false) - { - this.items[this.items.Count - 1 - i].HasRightGroupBorder = true; - rightset = true; - } - else - { - this.items[this.items.Count - 1 - i].HasRightGroupBorder = false; - } - } - } - - #endregion - - #region Override - - /// - /// Invoked when an unhandled System.Windows.UIElement.MouseLeftButtonUp�routed event - /// reaches an element in its route that is derived from this class. Implement this method to - /// add class handling for this event. - /// - /// The System.Windows.Input.MouseButtonEventArgs that contains the event data. - /// The event data reports that the left mouse button was released. - protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e) - { - var firstVisibleItem = this.FirstVisibleItem; - - if (e.ClickCount == 1 - && firstVisibleItem != null) - { - if (firstVisibleItem.TabControlParent != null) - { - var currentSelectedItem = firstVisibleItem.TabControlParent.SelectedItem as RibbonTabItem; - - if (currentSelectedItem != null) - { - currentSelectedItem.IsSelected = false; - } - } - - e.Handled = true; - - if (firstVisibleItem.TabControlParent != null) - { - if (firstVisibleItem.TabControlParent.IsMinimized) - { - firstVisibleItem.TabControlParent.IsMinimized = false; - } - - firstVisibleItem.IsSelected = true; - } - } - - base.OnMouseLeftButtonUp(e); - } - - /// - /// Raises the MouseDoubleClick routed event - /// - /// The event data - protected override void OnMouseDoubleClick(MouseButtonEventArgs e) - { - base.OnMouseDoubleClick(e); - - if (this.parentWidow == null) - { - return; - } - - this.parentWidow.WindowState = this.parentWidow.WindowState == WindowState.Maximized - ? WindowState.Normal - : WindowState.Maximized; - } - - #endregion - - /// - /// Updates the Visibility of the inner container - /// - private void UpdateInnerVisibility() - { - this.InnerVisibility = this.Visibility == Visibility.Visible && this.Items.Any(item => item.Visibility == Visibility.Visible) ? Visibility.Visible : Visibility.Collapsed; - } - - private void OnParentWindowStateChanged(object sender, EventArgs e) - { - this.IsWindowMaximized = this.parentWidow.WindowState == WindowState.Maximized; - } - } -} +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; + +namespace Fluent +{ + /// + /// Represents contextual tab group + /// + public class RibbonContextualTabGroup : Control + { + #region Fields + + // Collection of ribbon tab items + private readonly List items = new List(); + + private Window parentWidow; + + #endregion + + #region Properties + + /// + /// Gets or sets group header + /// + public string Header + { + get { return (string)this.GetValue(HeaderProperty); } + set { this.SetValue(HeaderProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Header. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty HeaderProperty = + DependencyProperty.Register("Header", typeof(string), typeof(RibbonContextualTabGroup), + new UIPropertyMetadata("RibbonContextualTabGroup", OnHeaderChanged)); + + /// + /// Handles header chages + /// + /// Object + /// The event data. + private static void OnHeaderChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + } + + /// + /// Gets collection of tab items + /// + public List Items + { + get { return this.items; } + } + + /// + /// Gets or sets a value indicating whether parent window is maximized + /// + public bool IsWindowMaximized + { + get { return (bool)this.GetValue(IsWindowMaximizedProperty); } + set { this.SetValue(IsWindowMaximizedProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for IsWindowMaximized. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IsWindowMaximizedProperty = + DependencyProperty.Register("IsWindowMaximized", typeof(bool), typeof(RibbonContextualTabGroup), new UIPropertyMetadata(false)); + + /// + /// Gets or sets the visibility this group for internal use (this enables us to hide this group when all items in this group are hidden) + /// + public Visibility InnerVisibility + { + get { return (Visibility)this.GetValue(InnerVisibilityProperty); } + private set { this.SetValue(InnerVisibilityPropertyKey, value); } + } + + private static readonly DependencyPropertyKey InnerVisibilityPropertyKey = + DependencyProperty.RegisterReadOnly("InnerVisibility", typeof(Visibility), typeof(RibbonContextualTabGroup), new UIPropertyMetadata(Visibility.Visible)); + + /// + /// Using a DependencyProperty as the backing store for InnerVisibility. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty InnerVisibilityProperty = InnerVisibilityPropertyKey.DependencyProperty; + + /// + /// Gets the first visible TabItem in this group + /// + public RibbonTabItem FirstVisibleItem + { + get + { + return this.GetFirstVisibleItem(); + } + } + + /// + /// Gets the last visible TabItem in this group + /// + public RibbonTabItem LastVisibleItem + { + get + { + return this.GetLastVisibleItem(); + } + } + + #endregion + + #region Initialization + + /// + /// Static constructor + /// + [SuppressMessage("Microsoft.Performance", "CA1810")] + static RibbonContextualTabGroup() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(RibbonContextualTabGroup), new FrameworkPropertyMetadata(typeof(RibbonContextualTabGroup))); + VisibilityProperty.OverrideMetadata(typeof(RibbonContextualTabGroup), new PropertyMetadata(Visibility.Collapsed, OnVisibilityChanged)); + StyleProperty.OverrideMetadata(typeof(RibbonContextualTabGroup), new FrameworkPropertyMetadata(null, OnCoerceStyle)); + } + + // Coerce object style + static object OnCoerceStyle(DependencyObject d, object basevalue) + { + if (basevalue == null) + { + basevalue = ((FrameworkElement)d).TryFindResource(typeof(RibbonContextualTabGroup)); + } + + return basevalue; + } + + /// + /// Handles visibility prioperty changed + /// + /// Object + /// The event data + private static void OnVisibilityChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var group = (RibbonContextualTabGroup)d; + + foreach (var tab in group.Items) + { + tab.Visibility = group.Visibility; + } + + group.UpdateInnerVisiblityAndGroupBorders(); + + var titleBar = group.Parent as RibbonTitleBar; + + if (titleBar != null) + { + titleBar.InvalidateMeasure(); + } + } + + /// + /// Default constructor + /// + public RibbonContextualTabGroup() + { + this.Loaded += this.OnLoaded; + this.Unloaded += this.OnUnloaded; + } + + private void OnLoaded(object sender, RoutedEventArgs e) + { + this.parentWidow = Window.GetWindow(this); + + this.SubscribeEvents(); + this.UpdateInnerVisibility(); + + if (this.parentWidow != null) + { + this.IsWindowMaximized = this.parentWidow.WindowState == WindowState.Maximized; + } + } + + private void OnUnloaded(object sender, RoutedEventArgs e) + { + this.UnSubscribeEvents(); + + this.parentWidow = null; + } + + private void SubscribeEvents() + { + // Always unsubscribe events to ensure we don't subscribe twice + this.UnSubscribeEvents(); + + if (this.parentWidow != null) + { + this.parentWidow.StateChanged += this.OnParentWindowStateChanged; + } + } + + private void UnSubscribeEvents() + { + if (this.parentWidow != null) + { + this.parentWidow.StateChanged -= this.OnParentWindowStateChanged; + } + } + + #endregion + + #region Internal Methods + + /// + /// Appends tab item + /// + /// Ribbon tab item + internal void AppendTabItem(RibbonTabItem item) + { + this.Items.Add(item); + this.UpdateInnerVisiblityAndGroupBorders(); + } + + /// + /// Removes tab item + /// + /// Ribbon tab item + internal void RemoveTabItem(RibbonTabItem item) + { + this.Items.Remove(item); + this.UpdateInnerVisiblityAndGroupBorders(); + } + + private RibbonTabItem GetFirstVisibleItem() + { + return this.items.FirstOrDefault(item => item.Visibility == Visibility.Visible); + } + + private RibbonTabItem GetLastVisibleItem() + { + return this.items.LastOrDefault(item => item.Visibility == Visibility.Visible); + } + + /// + /// Updates the group border + /// + public void UpdateInnerVisiblityAndGroupBorders() + { + this.UpdateInnerVisibility(); + + var leftset = false; + var rightset = false; + + for (var i = 0; i < this.items.Count; i++) + { + //if (i == 0) items[i].HasLeftGroupBorder = true; + //else items[i].HasLeftGroupBorder = false; + //if (i == items.Count - 1) items[i].HasRightGroupBorder = true; + //else items[i].HasRightGroupBorder = false; + + //Workaround so you can have inivisible Tabs on a Group + if (this.items[i].Visibility == Visibility.Visible + && leftset == false) + { + this.items[i].HasLeftGroupBorder = true; + leftset = true; + } + else + { + this.items[i].HasLeftGroupBorder = false; + } + + if (this.items[this.items.Count - 1 - i].Visibility == Visibility.Visible + && rightset == false) + { + this.items[this.items.Count - 1 - i].HasRightGroupBorder = true; + rightset = true; + } + else + { + this.items[this.items.Count - 1 - i].HasRightGroupBorder = false; + } + } + } + + #endregion + + #region Override + + /// + /// Invoked when an unhandled System.Windows.UIElement.MouseLeftButtonUp�routed event + /// reaches an element in its route that is derived from this class. Implement this method to + /// add class handling for this event. + /// + /// The System.Windows.Input.MouseButtonEventArgs that contains the event data. + /// The event data reports that the left mouse button was released. + protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e) + { + var firstVisibleItem = this.FirstVisibleItem; + + if (e.ClickCount == 1 + && firstVisibleItem != null) + { + if (firstVisibleItem.TabControlParent != null) + { + var currentSelectedItem = firstVisibleItem.TabControlParent.SelectedItem as RibbonTabItem; + + if (currentSelectedItem != null) + { + currentSelectedItem.IsSelected = false; + } + } + + e.Handled = true; + + if (firstVisibleItem.TabControlParent != null) + { + if (firstVisibleItem.TabControlParent.IsMinimized) + { + firstVisibleItem.TabControlParent.IsMinimized = false; + } + + firstVisibleItem.IsSelected = true; + } + } + + base.OnMouseLeftButtonUp(e); + } + + /// + /// Raises the MouseDoubleClick routed event + /// + /// The event data + protected override void OnMouseDoubleClick(MouseButtonEventArgs e) + { + base.OnMouseDoubleClick(e); + + if (this.parentWidow == null) + { + return; + } + + this.parentWidow.WindowState = this.parentWidow.WindowState == WindowState.Maximized + ? WindowState.Normal + : WindowState.Maximized; + } + + #endregion + + /// + /// Updates the Visibility of the inner container + /// + private void UpdateInnerVisibility() + { + this.InnerVisibility = this.Visibility == Visibility.Visible && this.Items.Any(item => item.Visibility == Visibility.Visible) ? Visibility.Visible : Visibility.Collapsed; + } + + private void OnParentWindowStateChanged(object sender, EventArgs e) + { + this.IsWindowMaximized = this.parentWidow.WindowState == WindowState.Maximized; + } + } +} diff --git a/Fluent/Controls/RibbonControl.cs b/Fluent.Ribbon/Controls/RibbonControl.cs similarity index 97% rename from Fluent/Controls/RibbonControl.cs rename to Fluent.Ribbon/Controls/RibbonControl.cs index 91160b313..b76dd55cc 100644 --- a/Fluent/Controls/RibbonControl.cs +++ b/Fluent.Ribbon/Controls/RibbonControl.cs @@ -1,510 +1,510 @@ -using System; -using System.ComponentModel; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.InteropServices; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Controls.Primitives; -using System.Windows.Data; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Shapes; - -namespace Fluent -{ - using Fluent.Internal; - using Fluent.Metro.Native; - - /// - /// Represent base class for Fluent controls - /// - public abstract class RibbonControl : Control, ICommandSource, IQuickAccessItemProvider, IRibbonControl - { - #region KeyTip - - /// - /// Gets or sets KeyTip for element. - /// - public string KeyTip - { - get { return (string)this.GetValue(KeyTipProperty); } - set { this.SetValue(KeyTipProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Keys. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty KeyTipProperty = Fluent.KeyTip.KeysProperty.AddOwner(typeof(RibbonControl)); - - #endregion - - #region Header - - /// - /// Gets or sets element header - /// - public object Header - { - get { return (string)this.GetValue(HeaderProperty); } - set { this.SetValue(HeaderProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Header. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty HeaderProperty = - DependencyProperty.Register("Header", typeof(object), typeof(RibbonControl), new UIPropertyMetadata(null)); - - #endregion - - #region Icon - - /// - /// Gets or sets Icon for the element - /// - public object Icon - { - get { return this.GetValue(IconProperty); } - set { this.SetValue(IconProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Icon. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IconProperty = - DependencyProperty.Register("Icon", typeof(object), typeof(RibbonControl), new UIPropertyMetadata(null, OnIconChanged)); - - private static void OnIconChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - RibbonControl element = d as RibbonControl; - FrameworkElement oldElement = e.OldValue as FrameworkElement; - if (oldElement != null) element.RemoveLogicalChild(oldElement); - FrameworkElement newElement = e.NewValue as FrameworkElement; - if (newElement != null) element.AddLogicalChild(newElement); - } - - #endregion - - #region Command - - private bool currentCanExecute = true; - - /// - /// Gets or sets the command to invoke when this button is pressed. This is a dependency property. - /// - [Category("Action"), Localizability(LocalizationCategory.NeverLocalize), Bindable(true)] - public ICommand Command - { - get - { - return (ICommand)this.GetValue(CommandProperty); - } - set - { - this.SetValue(CommandProperty, value); - } - } - - /// - /// Gets or sets the parameter to pass to the System.Windows.Controls.Primitives.ButtonBase.Command property. This is a dependency property. - /// - [Bindable(true), Localizability(LocalizationCategory.NeverLocalize), Category("Action")] - public object CommandParameter - { - get - { - return this.GetValue(CommandParameterProperty); - } - set - { - this.SetValue(CommandParameterProperty, value); - } - } - - /// - /// Gets or sets the element on which to raise the specified command. This is a dependency property. - /// - [Bindable(true), Category("Action")] - public IInputElement CommandTarget - { - get - { - return (IInputElement)this.GetValue(CommandTargetProperty); - } - set - { - this.SetValue(CommandTargetProperty, value); - } - } - - /// - /// Identifies the CommandParameter dependency property. - /// - public static readonly DependencyProperty CommandParameterProperty = ButtonBase.CommandParameterProperty.AddOwner(typeof(RibbonControl), new FrameworkPropertyMetadata(null)); - /// - /// Identifies the routed Command dependency property. - /// - public static readonly DependencyProperty CommandProperty = ButtonBase.CommandProperty.AddOwner(typeof(RibbonControl), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnCommandChanged))); - - /// - /// Identifies the CommandTarget dependency property. - /// - public static readonly DependencyProperty CommandTargetProperty = ButtonBase.CommandTargetProperty.AddOwner(typeof(RibbonControl), new FrameworkPropertyMetadata(null)); - - /// - /// Handles Command changed - /// - /// - /// - private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var control = d as RibbonControl; - - if (control == null) - { - return; - } - - var oldCommand = e.OldValue as ICommand; - if (oldCommand != null) - { - oldCommand.CanExecuteChanged -= control.OnCommandCanExecuteChanged; - } - - var newCommand = e.NewValue as ICommand; - if (newCommand != null) - { - newCommand.CanExecuteChanged += control.OnCommandCanExecuteChanged; - - var routedUiCommand = e.NewValue as RoutedUICommand; - if (routedUiCommand != null - && control.Header == null) - { - control.Header = routedUiCommand.Text; - } - } - - control.UpdateCanExecute(); - } - /// - /// Handles Command CanExecute changed - /// - /// - /// - private void OnCommandCanExecuteChanged(object sender, EventArgs e) - { - this.UpdateCanExecute(); - } - - private void UpdateCanExecute() - { - var canExecute = this.Command != null - && this.CanExecuteCommand(); - - if (this.currentCanExecute != canExecute) - { - this.currentCanExecute = canExecute; - this.CoerceValue(IsEnabledProperty); - } - } - - /// - /// Execute command - /// - protected void ExecuteCommand() - { - CommandHelper.Execute(this.Command, this.CommandParameter, this.CommandTarget); - } - - /// - /// Determines whether the Command can be executed - /// - /// Returns Command CanExecute - protected bool CanExecuteCommand() - { - return CommandHelper.CanExecute(this.Command, this.CommandParameter, this.CommandTarget); - } - - #endregion - - #region IsEnabled - - /// - /// Gets a value that becomes the return - /// value of IsEnabled in derived classes. - /// - /// - /// true if the element is enabled; otherwise, false. - /// - protected override bool IsEnabledCore - { - get - { - return (base.IsEnabledCore && (this.currentCanExecute || this.Command == null)); - } - } - - #endregion - - #region Size - - /// - /// Gets or sets Size for the element. - /// - public RibbonControlSize Size - { - get { return (RibbonControlSize)this.GetValue(SizeProperty); } - set { this.SetValue(SizeProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Size. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty SizeProperty = RibbonProperties.SizeProperty.AddOwner(typeof(RibbonControl)); - - #endregion - - #region SizeDefinition - - /// - /// Gets or sets SizeDefinition for element. - /// - public RibbonControlSizeDefinition SizeDefinition - { - get { return (RibbonControlSizeDefinition)this.GetValue(SizeDefinitionProperty); } - set { this.SetValue(SizeDefinitionProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for SizeDefinition. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty SizeDefinitionProperty = RibbonProperties.SizeDefinitionProperty.AddOwner(typeof(RibbonControl)); - - #endregion - - #region Constructors - - /// - /// Static constructor - /// - [SuppressMessage("Microsoft.Performance", "CA1810")] - static RibbonControl() - { - Type type = typeof(RibbonControl); - ContextMenuService.Attach(type); - ToolTipService.Attach(type); - } - - /// - /// Default Constructor - /// - protected RibbonControl() - { - ContextMenuService.Coerce(this); - } - - #endregion - - #region QuickAccess - - /// - /// Gets control which represents shortcut item. - /// This item MUST be syncronized with the original - /// and send command to original one control. - /// - /// Control which represents shortcut item - public abstract FrameworkElement CreateQuickAccessItem(); - - /// - /// Binds default properties of control to quick access element - /// - /// Toolbar item - /// Source item - public static void BindQuickAccessItem(FrameworkElement source, FrameworkElement element) - { - Bind(source, element, "DataContext", DataContextProperty, BindingMode.OneWay); - - if (source is ICommandSource) - { - if (source is MenuItem) - { - Bind(source, element, "CommandParameter", ButtonBase.CommandParameterProperty, BindingMode.OneWay); - Bind(source, element, "CommandTarget", System.Windows.Controls.MenuItem.CommandTargetProperty, BindingMode.OneWay); - Bind(source, element, "Command", System.Windows.Controls.MenuItem.CommandProperty, BindingMode.OneWay); - } - else - { - Bind(source, element, "CommandParameter", ButtonBase.CommandParameterProperty, BindingMode.OneWay); - Bind(source, element, "CommandTarget", ButtonBase.CommandTargetProperty, BindingMode.OneWay); - Bind(source, element, "Command", ButtonBase.CommandProperty, BindingMode.OneWay); - } - } - - Bind(source, element, "ToolTip", ToolTipProperty, BindingMode.OneWay); - - Bind(source, element, "FontFamily", FontFamilyProperty, BindingMode.OneWay); - Bind(source, element, "FontSize", FontSizeProperty, BindingMode.OneWay); - Bind(source, element, "FontStretch", FontStretchProperty, BindingMode.OneWay); - Bind(source, element, "FontStyle", FontStyleProperty, BindingMode.OneWay); - Bind(source, element, "FontWeight", FontWeightProperty, BindingMode.OneWay); - - Bind(source, element, "Foreground", ForegroundProperty, BindingMode.OneWay); - Bind(source, element, "IsEnabled", IsEnabledProperty, BindingMode.OneWay); - Bind(source, element, "Opacity", OpacityProperty, BindingMode.OneWay); - Bind(source, element, "SnapsToDevicePixels", SnapsToDevicePixelsProperty, BindingMode.OneWay); - - Bind(source, element, new PropertyPath(FocusManager.IsFocusScopeProperty), FocusManager.IsFocusScopeProperty, BindingMode.OneWay); - - var sourceControl = source as IRibbonControl; - if (sourceControl != null) - { - if (sourceControl.Icon != null) - { - var iconVisual = sourceControl.Icon as Visual; - if (iconVisual != null) - { - var rect = new Rectangle(); - rect.Width = 16; - rect.Height = 16; - rect.Fill = new VisualBrush(iconVisual); - ((IRibbonControl) element).Icon = rect; - } - else - { - Bind(source, element, "Icon", IconProperty, BindingMode.OneWay); - } - } - - if (sourceControl.Header != null) - { - Bind(source, element, "Header", HeaderProperty, BindingMode.OneWay); - } - } - - RibbonProperties.SetSize(element, RibbonControlSize.Small); - } - - /// - /// Gets or sets whether control can be added to quick access toolbar - /// - public bool CanAddToQuickAccessToolBar - { - get { return (bool)this.GetValue(CanAddToQuickAccessToolBarProperty); } - set { this.SetValue(CanAddToQuickAccessToolBarProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for CanAddToQuickAccessToolBar. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty CanAddToQuickAccessToolBarProperty = - DependencyProperty.Register("CanAddToQuickAccessToolBar", typeof(bool), typeof(RibbonControl), new UIPropertyMetadata(true, OnCanAddToQuickAccessToolbarChanged)); - - /// - /// Occurs then CanAddToQuickAccessToolBar property changed - /// - /// - /// - public static void OnCanAddToQuickAccessToolbarChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - d.CoerceValue(ContextMenuProperty); - } - - #endregion - - #region Binding - - internal static void Bind(object source, FrameworkElement target, string path, DependencyProperty property, BindingMode mode) - { - Bind(source, target, new PropertyPath(path), property, mode); - } - - internal static void Bind(object source, FrameworkElement target, PropertyPath path, DependencyProperty property, BindingMode mode) - { - var binding = new Binding - { - Path = path, - Source = source, - Mode = mode - }; - target.SetBinding(property, binding); - } - - #endregion - - #region Methods - - /// - /// Handles key tip pressed - /// - public virtual void OnKeyTipPressed() - { - } - - /// - /// Handles back navigation with KeyTips - /// - public virtual void OnKeyTipBack() - { - } - - #endregion - - #region StaticMethods - - /// - /// Returns screen workarea in witch control is placed - /// - /// Control - /// Workarea in witch control is placed - public static Rect GetControlWorkArea(FrameworkElement control) - { - var tabItemPos = control.PointToScreen(new Point(0, 0)); - var tabItemRect = new RECT(); - tabItemRect.left = (int)tabItemPos.X; - tabItemRect.top = (int)tabItemPos.Y; - tabItemRect.right = (int)tabItemPos.X + (int)control.ActualWidth; - tabItemRect.bottom = (int)tabItemPos.Y + (int)control.ActualHeight; - const uint MONITOR_DEFAULTTONEAREST = 0x00000002; - var monitor = NativeMethods.MonitorFromRect(ref tabItemRect, MONITOR_DEFAULTTONEAREST); - if (monitor != IntPtr.Zero) - { - var monitorInfo = new MONITORINFO(); - monitorInfo.cbSize = Marshal.SizeOf(monitorInfo); - UnsafeNativeMethods.GetMonitorInfo(monitor, monitorInfo); - return new Rect(monitorInfo.rcWork.left, monitorInfo.rcWork.top, monitorInfo.rcWork.right - monitorInfo.rcWork.left, monitorInfo.rcWork.bottom - monitorInfo.rcWork.top); - } - return new Rect(); - } - - /// - /// Returns monitor in witch control is placed - /// - /// Control - /// Workarea in witch control is placed - public static Rect GetControlMonitor(FrameworkElement control) - { - var tabItemPos = control.PointToScreen(new Point(0, 0)); - var tabItemRect = new RECT(); - tabItemRect.left = (int)tabItemPos.X; - tabItemRect.top = (int)tabItemPos.Y; - tabItemRect.right = (int)tabItemPos.X + (int)control.ActualWidth; - tabItemRect.bottom = (int)tabItemPos.Y + (int)control.ActualHeight; - const uint MONITOR_DEFAULTTONEAREST = 0x00000002; - var monitor = NativeMethods.MonitorFromRect(ref tabItemRect, MONITOR_DEFAULTTONEAREST); - if (monitor != IntPtr.Zero) - { - var monitorInfo = new MONITORINFO(); - monitorInfo.cbSize = Marshal.SizeOf(monitorInfo); - UnsafeNativeMethods.GetMonitorInfo(monitor, monitorInfo); - return new Rect(monitorInfo.rcMonitor.left, monitorInfo.rcMonitor.top, monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left, monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top); - } - return new Rect(); - } - - #endregion - } +using System; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Data; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Shapes; + +namespace Fluent +{ + using Fluent.Internal; + using Fluent.Metro.Native; + + /// + /// Represent base class for Fluent controls + /// + public abstract class RibbonControl : Control, ICommandSource, IQuickAccessItemProvider, IRibbonControl + { + #region KeyTip + + /// + /// Gets or sets KeyTip for element. + /// + public string KeyTip + { + get { return (string)this.GetValue(KeyTipProperty); } + set { this.SetValue(KeyTipProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Keys. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty KeyTipProperty = Fluent.KeyTip.KeysProperty.AddOwner(typeof(RibbonControl)); + + #endregion + + #region Header + + /// + /// Gets or sets element header + /// + public object Header + { + get { return (string)this.GetValue(HeaderProperty); } + set { this.SetValue(HeaderProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Header. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty HeaderProperty = + DependencyProperty.Register("Header", typeof(object), typeof(RibbonControl), new UIPropertyMetadata(null)); + + #endregion + + #region Icon + + /// + /// Gets or sets Icon for the element + /// + public object Icon + { + get { return this.GetValue(IconProperty); } + set { this.SetValue(IconProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Icon. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IconProperty = + DependencyProperty.Register("Icon", typeof(object), typeof(RibbonControl), new UIPropertyMetadata(null, OnIconChanged)); + + private static void OnIconChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + RibbonControl element = d as RibbonControl; + FrameworkElement oldElement = e.OldValue as FrameworkElement; + if (oldElement != null) element.RemoveLogicalChild(oldElement); + FrameworkElement newElement = e.NewValue as FrameworkElement; + if (newElement != null) element.AddLogicalChild(newElement); + } + + #endregion + + #region Command + + private bool currentCanExecute = true; + + /// + /// Gets or sets the command to invoke when this button is pressed. This is a dependency property. + /// + [Category("Action"), Localizability(LocalizationCategory.NeverLocalize), Bindable(true)] + public ICommand Command + { + get + { + return (ICommand)this.GetValue(CommandProperty); + } + set + { + this.SetValue(CommandProperty, value); + } + } + + /// + /// Gets or sets the parameter to pass to the System.Windows.Controls.Primitives.ButtonBase.Command property. This is a dependency property. + /// + [Bindable(true), Localizability(LocalizationCategory.NeverLocalize), Category("Action")] + public object CommandParameter + { + get + { + return this.GetValue(CommandParameterProperty); + } + set + { + this.SetValue(CommandParameterProperty, value); + } + } + + /// + /// Gets or sets the element on which to raise the specified command. This is a dependency property. + /// + [Bindable(true), Category("Action")] + public IInputElement CommandTarget + { + get + { + return (IInputElement)this.GetValue(CommandTargetProperty); + } + set + { + this.SetValue(CommandTargetProperty, value); + } + } + + /// + /// Identifies the CommandParameter dependency property. + /// + public static readonly DependencyProperty CommandParameterProperty = ButtonBase.CommandParameterProperty.AddOwner(typeof(RibbonControl), new FrameworkPropertyMetadata(null)); + /// + /// Identifies the routed Command dependency property. + /// + public static readonly DependencyProperty CommandProperty = ButtonBase.CommandProperty.AddOwner(typeof(RibbonControl), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnCommandChanged))); + + /// + /// Identifies the CommandTarget dependency property. + /// + public static readonly DependencyProperty CommandTargetProperty = ButtonBase.CommandTargetProperty.AddOwner(typeof(RibbonControl), new FrameworkPropertyMetadata(null)); + + /// + /// Handles Command changed + /// + /// + /// + private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var control = d as RibbonControl; + + if (control == null) + { + return; + } + + var oldCommand = e.OldValue as ICommand; + if (oldCommand != null) + { + oldCommand.CanExecuteChanged -= control.OnCommandCanExecuteChanged; + } + + var newCommand = e.NewValue as ICommand; + if (newCommand != null) + { + newCommand.CanExecuteChanged += control.OnCommandCanExecuteChanged; + + var routedUiCommand = e.NewValue as RoutedUICommand; + if (routedUiCommand != null + && control.Header == null) + { + control.Header = routedUiCommand.Text; + } + } + + control.UpdateCanExecute(); + } + /// + /// Handles Command CanExecute changed + /// + /// + /// + private void OnCommandCanExecuteChanged(object sender, EventArgs e) + { + this.UpdateCanExecute(); + } + + private void UpdateCanExecute() + { + var canExecute = this.Command != null + && this.CanExecuteCommand(); + + if (this.currentCanExecute != canExecute) + { + this.currentCanExecute = canExecute; + this.CoerceValue(IsEnabledProperty); + } + } + + /// + /// Execute command + /// + protected void ExecuteCommand() + { + CommandHelper.Execute(this.Command, this.CommandParameter, this.CommandTarget); + } + + /// + /// Determines whether the Command can be executed + /// + /// Returns Command CanExecute + protected bool CanExecuteCommand() + { + return CommandHelper.CanExecute(this.Command, this.CommandParameter, this.CommandTarget); + } + + #endregion + + #region IsEnabled + + /// + /// Gets a value that becomes the return + /// value of IsEnabled in derived classes. + /// + /// + /// true if the element is enabled; otherwise, false. + /// + protected override bool IsEnabledCore + { + get + { + return (base.IsEnabledCore && (this.currentCanExecute || this.Command == null)); + } + } + + #endregion + + #region Size + + /// + /// Gets or sets Size for the element. + /// + public RibbonControlSize Size + { + get { return (RibbonControlSize)this.GetValue(SizeProperty); } + set { this.SetValue(SizeProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Size. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty SizeProperty = RibbonProperties.SizeProperty.AddOwner(typeof(RibbonControl)); + + #endregion + + #region SizeDefinition + + /// + /// Gets or sets SizeDefinition for element. + /// + public RibbonControlSizeDefinition SizeDefinition + { + get { return (RibbonControlSizeDefinition)this.GetValue(SizeDefinitionProperty); } + set { this.SetValue(SizeDefinitionProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for SizeDefinition. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty SizeDefinitionProperty = RibbonProperties.SizeDefinitionProperty.AddOwner(typeof(RibbonControl)); + + #endregion + + #region Constructors + + /// + /// Static constructor + /// + [SuppressMessage("Microsoft.Performance", "CA1810")] + static RibbonControl() + { + Type type = typeof(RibbonControl); + ContextMenuService.Attach(type); + ToolTipService.Attach(type); + } + + /// + /// Default Constructor + /// + protected RibbonControl() + { + ContextMenuService.Coerce(this); + } + + #endregion + + #region QuickAccess + + /// + /// Gets control which represents shortcut item. + /// This item MUST be syncronized with the original + /// and send command to original one control. + /// + /// Control which represents shortcut item + public abstract FrameworkElement CreateQuickAccessItem(); + + /// + /// Binds default properties of control to quick access element + /// + /// Toolbar item + /// Source item + public static void BindQuickAccessItem(FrameworkElement source, FrameworkElement element) + { + Bind(source, element, "DataContext", DataContextProperty, BindingMode.OneWay); + + if (source is ICommandSource) + { + if (source is MenuItem) + { + Bind(source, element, "CommandParameter", ButtonBase.CommandParameterProperty, BindingMode.OneWay); + Bind(source, element, "CommandTarget", System.Windows.Controls.MenuItem.CommandTargetProperty, BindingMode.OneWay); + Bind(source, element, "Command", System.Windows.Controls.MenuItem.CommandProperty, BindingMode.OneWay); + } + else + { + Bind(source, element, "CommandParameter", ButtonBase.CommandParameterProperty, BindingMode.OneWay); + Bind(source, element, "CommandTarget", ButtonBase.CommandTargetProperty, BindingMode.OneWay); + Bind(source, element, "Command", ButtonBase.CommandProperty, BindingMode.OneWay); + } + } + + Bind(source, element, "ToolTip", ToolTipProperty, BindingMode.OneWay); + + Bind(source, element, "FontFamily", FontFamilyProperty, BindingMode.OneWay); + Bind(source, element, "FontSize", FontSizeProperty, BindingMode.OneWay); + Bind(source, element, "FontStretch", FontStretchProperty, BindingMode.OneWay); + Bind(source, element, "FontStyle", FontStyleProperty, BindingMode.OneWay); + Bind(source, element, "FontWeight", FontWeightProperty, BindingMode.OneWay); + + Bind(source, element, "Foreground", ForegroundProperty, BindingMode.OneWay); + Bind(source, element, "IsEnabled", IsEnabledProperty, BindingMode.OneWay); + Bind(source, element, "Opacity", OpacityProperty, BindingMode.OneWay); + Bind(source, element, "SnapsToDevicePixels", SnapsToDevicePixelsProperty, BindingMode.OneWay); + + Bind(source, element, new PropertyPath(FocusManager.IsFocusScopeProperty), FocusManager.IsFocusScopeProperty, BindingMode.OneWay); + + var sourceControl = source as IRibbonControl; + if (sourceControl != null) + { + if (sourceControl.Icon != null) + { + var iconVisual = sourceControl.Icon as Visual; + if (iconVisual != null) + { + var rect = new Rectangle(); + rect.Width = 16; + rect.Height = 16; + rect.Fill = new VisualBrush(iconVisual); + ((IRibbonControl) element).Icon = rect; + } + else + { + Bind(source, element, "Icon", IconProperty, BindingMode.OneWay); + } + } + + if (sourceControl.Header != null) + { + Bind(source, element, "Header", HeaderProperty, BindingMode.OneWay); + } + } + + RibbonProperties.SetSize(element, RibbonControlSize.Small); + } + + /// + /// Gets or sets whether control can be added to quick access toolbar + /// + public bool CanAddToQuickAccessToolBar + { + get { return (bool)this.GetValue(CanAddToQuickAccessToolBarProperty); } + set { this.SetValue(CanAddToQuickAccessToolBarProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for CanAddToQuickAccessToolBar. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty CanAddToQuickAccessToolBarProperty = + DependencyProperty.Register("CanAddToQuickAccessToolBar", typeof(bool), typeof(RibbonControl), new UIPropertyMetadata(true, OnCanAddToQuickAccessToolbarChanged)); + + /// + /// Occurs then CanAddToQuickAccessToolBar property changed + /// + /// + /// + public static void OnCanAddToQuickAccessToolbarChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + d.CoerceValue(ContextMenuProperty); + } + + #endregion + + #region Binding + + internal static void Bind(object source, FrameworkElement target, string path, DependencyProperty property, BindingMode mode) + { + Bind(source, target, new PropertyPath(path), property, mode); + } + + internal static void Bind(object source, FrameworkElement target, PropertyPath path, DependencyProperty property, BindingMode mode) + { + var binding = new Binding + { + Path = path, + Source = source, + Mode = mode + }; + target.SetBinding(property, binding); + } + + #endregion + + #region Methods + + /// + /// Handles key tip pressed + /// + public virtual void OnKeyTipPressed() + { + } + + /// + /// Handles back navigation with KeyTips + /// + public virtual void OnKeyTipBack() + { + } + + #endregion + + #region StaticMethods + + /// + /// Returns screen workarea in witch control is placed + /// + /// Control + /// Workarea in witch control is placed + public static Rect GetControlWorkArea(FrameworkElement control) + { + var tabItemPos = control.PointToScreen(new Point(0, 0)); + var tabItemRect = new RECT(); + tabItemRect.left = (int)tabItemPos.X; + tabItemRect.top = (int)tabItemPos.Y; + tabItemRect.right = (int)tabItemPos.X + (int)control.ActualWidth; + tabItemRect.bottom = (int)tabItemPos.Y + (int)control.ActualHeight; + const uint MONITOR_DEFAULTTONEAREST = 0x00000002; + var monitor = NativeMethods.MonitorFromRect(ref tabItemRect, MONITOR_DEFAULTTONEAREST); + if (monitor != IntPtr.Zero) + { + var monitorInfo = new MONITORINFO(); + monitorInfo.cbSize = Marshal.SizeOf(monitorInfo); + UnsafeNativeMethods.GetMonitorInfo(monitor, monitorInfo); + return new Rect(monitorInfo.rcWork.left, monitorInfo.rcWork.top, monitorInfo.rcWork.right - monitorInfo.rcWork.left, monitorInfo.rcWork.bottom - monitorInfo.rcWork.top); + } + return new Rect(); + } + + /// + /// Returns monitor in witch control is placed + /// + /// Control + /// Workarea in witch control is placed + public static Rect GetControlMonitor(FrameworkElement control) + { + var tabItemPos = control.PointToScreen(new Point(0, 0)); + var tabItemRect = new RECT(); + tabItemRect.left = (int)tabItemPos.X; + tabItemRect.top = (int)tabItemPos.Y; + tabItemRect.right = (int)tabItemPos.X + (int)control.ActualWidth; + tabItemRect.bottom = (int)tabItemPos.Y + (int)control.ActualHeight; + const uint MONITOR_DEFAULTTONEAREST = 0x00000002; + var monitor = NativeMethods.MonitorFromRect(ref tabItemRect, MONITOR_DEFAULTTONEAREST); + if (monitor != IntPtr.Zero) + { + var monitorInfo = new MONITORINFO(); + monitorInfo.cbSize = Marshal.SizeOf(monitorInfo); + UnsafeNativeMethods.GetMonitorInfo(monitor, monitorInfo); + return new Rect(monitorInfo.rcMonitor.left, monitorInfo.rcMonitor.top, monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left, monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top); + } + return new Rect(); + } + + #endregion + } } \ No newline at end of file diff --git a/Fluent/Controls/RibbonGroupBox.cs b/Fluent.Ribbon/Controls/RibbonGroupBox.cs similarity index 97% rename from Fluent/Controls/RibbonGroupBox.cs rename to Fluent.Ribbon/Controls/RibbonGroupBox.cs index 8acd13a1a..5253328c7 100644 --- a/Fluent/Controls/RibbonGroupBox.cs +++ b/Fluent.Ribbon/Controls/RibbonGroupBox.cs @@ -1,1122 +1,1122 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.ComponentModel; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Controls.Primitives; -using System.Windows.Data; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Shapes; - -namespace Fluent -{ - using Fluent.Internal; - - /// - /// RibbonGroup represents a logical group of controls as they appear on - /// a RibbonTab. These groups can resize its content - /// - [TemplatePart(Name = "PART_DialogLauncherButton", Type = typeof(Button))] - [TemplatePart(Name = "PART_Popup", Type = typeof(Popup))] - [TemplatePart(Name = "PART_UpPanel", Type = typeof(Panel))] - public class RibbonGroupBox : ItemsControl, IQuickAccessItemProvider, IDropDownControl, IKeyTipedControl, IHeaderedControl - { - #region Fields - - // up part - private Panel upPanel; - - private Panel parentPanel; - - // Freezed image (created during snapping) - private Image snappedImage; - - // Is visual currently snapped - private bool isSnapped; - - private readonly ItemContainerGeneratorAction updateChildSizesItemContainerGeneratorAction; - - #endregion - - #region Properties - - #region KeyTip - - /// - /// Gets or sets KeyTip for element. - /// - public string KeyTip - { - get { return (string)this.GetValue(KeyTipProperty); } - set { this.SetValue(KeyTipProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Keys. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty KeyTipProperty = Fluent.KeyTip.KeysProperty.AddOwner(typeof(RibbonGroupBox)); - - #endregion - - /// - /// Gets drop down popup - /// - public Popup DropDownPopup { get; private set; } - - /// - /// Gets a value indicating whether context menu is opened - /// - public bool IsContextMenuOpened { get; set; } - - #region State - - /// - /// Gets or sets the current state of the group - /// - public RibbonGroupBoxState State - { - get { return (RibbonGroupBoxState)this.GetValue(StateProperty); } - set { this.SetValue(StateProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for State. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty StateProperty = - DependencyProperty.Register("State", typeof(RibbonGroupBoxState), typeof(RibbonGroupBox), new UIPropertyMetadata(RibbonGroupBoxState.Large, StatePropertyChanged)); - - /// - /// On state property changed - /// - /// Object - /// The event data - static void StatePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var ribbonGroupBox = (RibbonGroupBox)d; - ribbonGroupBox.updateChildSizesItemContainerGeneratorAction.QueueAction(); - } - - private void UpdateChildSizes() - { - var groupBoxState = this.State == RibbonGroupBoxState.QuickAccess - ? RibbonGroupBoxState.Collapsed - : this.State; - - foreach (var item in this.Items) - { - var element = this.ItemContainerGenerator.ContainerFromItem(item); - - if (element == null) - { - continue; - } - - RibbonProperties.SetAppropriateSize(element, groupBoxState); - } - } - - #endregion - - #region Scale - - // Current scale index - private int scale; - - /// - /// Gets or sets scale index (for internal IRibbonScalableControl) - /// - internal int Scale - { - get { return this.scale; } - set - { - var difference = value - this.scale; - this.scale = value; - - for (var i = 0; i < Math.Abs(difference); i++) - { - if (difference > 0) - { - this.IncreaseScalableElement(); - } - else - { - this.DecreaseScalableElement(); - } - } - } - } - - // Finds and increase size of all scalable elements in the given group box - private void IncreaseScalableElement() - { - foreach (var item in this.Items) - { - var scalableRibbonControl = item as IScalableRibbonControl; - if (scalableRibbonControl == null) - { - continue; - } - scalableRibbonControl.Enlarge(); - } - } - - private void OnScalableControlScaled(object sender, EventArgs e) - { - this.TryClearCache(); - } - - private void TryClearCache() - { - if (!this.SuppressCacheReseting) - { - this.cachedMeasures.Clear(); - } - } - - /// - /// Gets or sets whether to reset cache when scalable control is scaled - /// - internal bool SuppressCacheReseting { get; set; } - - // Finds and decrease size of all scalable elements in the given group box - private void DecreaseScalableElement() - { - foreach (object item in this.Items) - { - IScalableRibbonControl scalableRibbonControl = item as IScalableRibbonControl; - if (scalableRibbonControl == null) continue; - scalableRibbonControl.Reduce(); - } - } - - private void UpdateScalableControlSubscribing() - { - this.UpdateScalableControlSubscribing(true); - } - - private void UpdateScalableControlSubscribing(bool registerEvents) - { - foreach (var scalableRibbonControl in this.Items.OfType()) - { - // Always unregister first to ensure that we don't subscribe twice - scalableRibbonControl.Scaled -= this.OnScalableControlScaled; - - if (registerEvents) - { - scalableRibbonControl.Scaled += this.OnScalableControlScaled; - } - } - } - - #endregion - - #region Header - - /// - /// Gets or sets group box header - /// - public string Header - { - get { return (string)this.GetValue(HeaderProperty); } - set { this.SetValue(HeaderProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Header. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty HeaderProperty = - DependencyProperty.Register("Header", typeof(string), typeof(RibbonGroupBox), new UIPropertyMetadata()); - - object IHeaderedControl.Header - { - get { return this.Header; } - - set { this.Header = (string)value; } - } - - #endregion - - #region IsLauncherVisible - - /// - /// Gets or sets dialog launcher button visibility - /// - public bool IsLauncherVisible - { - get { return (bool)this.GetValue(IsLauncherVisibleProperty); } - set { this.SetValue(IsLauncherVisibleProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for IsLauncherVisible. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IsLauncherVisibleProperty = - DependencyProperty.Register("IsLauncherVisible", typeof(bool), typeof(RibbonGroupBox), new UIPropertyMetadata(false)); - - #endregion - - #region LauncherKeys - - /// - /// Gets or sets key tip for dialog launcher button - /// - [DisplayName("DialogLauncher Keys"), - Category("KeyTips"), - Description("Key tip keys for dialog launcher button")] - public string LauncherKeys - { - get { return (string)this.GetValue(DialogLauncherButtonKeyTipKeysProperty); } - set { this.SetValue(DialogLauncherButtonKeyTipKeysProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for - /// LauncherKeys. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty DialogLauncherButtonKeyTipKeysProperty = - DependencyProperty.Register("LauncherKeys", - typeof(string), typeof(RibbonGroupBox), new UIPropertyMetadata(null, OnDialogLauncherButtonKeyTipKeysChanged)); - - static void OnDialogLauncherButtonKeyTipKeysChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - RibbonGroupBox ribbonGroupBox = (RibbonGroupBox)d; - if (ribbonGroupBox.LauncherButton != null) - { - ribbonGroupBox.LauncherButton.KeyTip = (string)e.NewValue; - } - } - - #endregion - - #region LauncherIcon - - /// - /// Gets or sets launcher button icon - /// - public object LauncherIcon - { - get { return (ImageSource)this.GetValue(LauncherIconProperty); } - set { this.SetValue(LauncherIconProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for LauncherIcon. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty LauncherIconProperty = - DependencyProperty.Register("LauncherIcon", typeof(object), typeof(RibbonGroupBox), new UIPropertyMetadata(null, OnIconChanged)); - - #endregion - - #region LauncherIcon - - /// - /// Gets or sets launcher button text - /// - public string LauncherText - { - get { return (string)this.GetValue(LauncherTextProperty); } - set { this.SetValue(LauncherTextProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for LauncherIcon. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty LauncherTextProperty = - DependencyProperty.Register("LauncherText", typeof(string), typeof(RibbonGroupBox), new UIPropertyMetadata(null)); - - #endregion - - #region LauncherCommand - - /// - /// Gets or sets the command to invoke when this button is pressed. This is a dependency property. - /// - [Category("Action"), Localizability(LocalizationCategory.NeverLocalize), Bindable(true)] - public ICommand LauncherCommand - { - get - { - return (ICommand)this.GetValue(LauncherCommandProperty); - } - set - { - this.SetValue(LauncherCommandProperty, value); - } - } - - /// - /// Gets or sets the parameter to pass to the System.Windows.Controls.Primitives.ButtonBase.Command property. This is a dependency property. - /// - [Bindable(true), Localizability(LocalizationCategory.NeverLocalize), Category("Action")] - public object LauncherCommandParameter - { - get - { - return this.GetValue(LauncherCommandParameterProperty); - } - set - { - this.SetValue(LauncherCommandParameterProperty, value); - } - } - - /// - /// Gets or sets the element on which to raise the specified command. This is a dependency property. - /// - [Bindable(true), Category("Action")] - public IInputElement LauncherCommandTarget - { - get - { - return (IInputElement)this.GetValue(LauncherCommandTargetProperty); - } - set - { - this.SetValue(LauncherCommandTargetProperty, value); - } - } - - /// - /// Identifies the System.Windows.Controls.Primitives.ButtonBase.CommandParameter dependency property. - /// - public static readonly DependencyProperty LauncherCommandParameterProperty = DependencyProperty.Register("LauncherCommandParameter", typeof(object), typeof(RibbonGroupBox), new FrameworkPropertyMetadata(null)); - /// - /// Identifies the routed System.Windows.Controls.Primitives.ButtonBase.Command dependency property. - /// - public static readonly DependencyProperty LauncherCommandProperty = DependencyProperty.Register("LauncherCommand", typeof(ICommand), typeof(RibbonGroupBox), new FrameworkPropertyMetadata(null)); - - /// - /// Identifies the System.Windows.Controls.Primitives.ButtonBase.CommandTarget dependency property. - /// - public static readonly DependencyProperty LauncherCommandTargetProperty = DependencyProperty.Register("LauncherCommandTarget", typeof(IInputElement), typeof(RibbonGroupBox), new FrameworkPropertyMetadata(null)); - - #endregion - - #region LauncherToolTip - - /// - /// Gets or sets launcher button tooltip - /// - public object LauncherToolTip - { - get { return (object)this.GetValue(LauncherToolTipProperty); } - set { this.SetValue(LauncherToolTipProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for LauncherToolTip. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty LauncherToolTipProperty = - DependencyProperty.Register("LauncherToolTip", typeof(object), typeof(RibbonGroupBox), new UIPropertyMetadata(null)); - - - - #endregion - - #region IsLauncherEnabled - - /// - /// Gets or sets whether launcher button is enabled - /// - public bool IsLauncherEnabled - { - get { return (bool)this.GetValue(IsLauncherEnabledProperty); } - set { this.SetValue(IsLauncherEnabledProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for IsLauncherEnabled. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IsLauncherEnabledProperty = - DependencyProperty.Register("IsLauncherEnabled", typeof(bool), typeof(RibbonGroupBox), new UIPropertyMetadata(true)); - - #endregion - - #region LauncherButton - - /// - /// Gets launcher button - /// - public Button LauncherButton - { - get { return (Button)this.GetValue(LauncherButtonProperty); } - private set { this.SetValue(LauncherButtonPropertyKey, value); } - } - - private static readonly DependencyPropertyKey LauncherButtonPropertyKey = - DependencyProperty.RegisterReadOnly("LauncherButton", typeof(Button), typeof(RibbonGroupBox), new UIPropertyMetadata(null)); - - /// - /// Using a DependencyProperty as the backing store for LauncherButton. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty LauncherButtonProperty = LauncherButtonPropertyKey.DependencyProperty; - - #endregion - - #region IsOpen - - /// - /// Gets or sets drop down popup visibility - /// - public bool IsDropDownOpen - { - get { return (bool)this.GetValue(IsDropDownOpenProperty); } - set { this.SetValue(IsDropDownOpenProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for IsOpen. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IsDropDownOpenProperty = DependencyProperty.Register("IsDropDownOpen", typeof(bool), typeof(RibbonGroupBox), new UIPropertyMetadata(false, OnIsOpenChanged, CoerceIsDropDownOpen)); - - private static object CoerceIsDropDownOpen(DependencyObject d, object basevalue) - { - RibbonGroupBox box = d as RibbonGroupBox; - if ((box.State != RibbonGroupBoxState.Collapsed) && (box.State != RibbonGroupBoxState.QuickAccess)) return false; - return basevalue; - } - - #endregion - - #region LogicalChildren - - /// - /// Gets an enumerator for the logical child objects of - /// the System.Windows.Controls.ItemsControl object. - /// - protected override IEnumerator LogicalChildren - { - get - { - foreach (var item in this.Items) - { - yield return item; - } - - if (this.LauncherButton != null) - { - yield return this.LauncherButton; - } - } - } - - #endregion - - #region Icon - - /// - /// Gets or sets icon - /// - public object Icon - //public ImageSource Icon - { - get { return this.GetValue(IconProperty); } - set { this.SetValue(IconProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Icon. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IconProperty = - RibbonControl.IconProperty.AddOwner(typeof(RibbonGroupBox), new UIPropertyMetadata(null, OnIconChanged)); - - - private static void OnIconChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - RibbonGroupBox element = d as RibbonGroupBox; - FrameworkElement oldElement = e.OldValue as FrameworkElement; - if (oldElement != null) element.RemoveLogicalChild(oldElement); - FrameworkElement newElement = e.NewValue as FrameworkElement; - if (newElement != null) element.AddLogicalChild(newElement); - } - - #endregion - - #endregion - - #region Events - - /// - /// Dialog launcher btton click event - /// - public event RoutedEventHandler LauncherClick; - - /// - /// Occurs when context menu is opened - /// - public event EventHandler DropDownOpened; - - /// - /// Occurs when context menu is closed - /// - public event EventHandler DropDownClosed; - - #endregion - - #region Initialize - - /// - /// Static constructor - /// - [SuppressMessage("Microsoft.Performance", "CA1810")] - static RibbonGroupBox() - { - DefaultStyleKeyProperty.OverrideMetadata(typeof(RibbonGroupBox), new FrameworkPropertyMetadata(typeof(RibbonGroupBox))); - VisibilityProperty.AddOwner(typeof(RibbonGroupBox), new FrameworkPropertyMetadata(OnVisibilityChanged)); - - PopupService.Attach(typeof(RibbonGroupBox)); - StyleProperty.OverrideMetadata(typeof(RibbonGroupBox), new FrameworkPropertyMetadata(null, OnCoerceStyle)); - - ContextMenuService.Attach(typeof(RibbonGroupBox)); - } - - // Coerce object style - private static object OnCoerceStyle(DependencyObject d, object basevalue) - { - if (basevalue == null) - { - basevalue = (d as FrameworkElement).TryFindResource(typeof(RibbonGroupBox)); - } - - return basevalue; - } - - // Handles visibility changed - private static void OnVisibilityChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var box = (d as RibbonGroupBox); - if (box != null) box.ClearCache(); - } - - /// - /// Default constructor - /// - public RibbonGroupBox() - { - this.ToolTip = new ToolTip(); - ((ToolTip)this.ToolTip).Template = null; - this.CoerceValue(ContextMenuProperty); - this.Focusable = false; - - this.Loaded += this.OnLoaded; - this.Unloaded += this.OnUnloaded; - - this.updateChildSizesItemContainerGeneratorAction = new ItemContainerGeneratorAction(this.ItemContainerGenerator, this.UpdateChildSizes); - } - - private void OnLoaded(object sender, RoutedEventArgs e) - { - this.SubscribeEvents(); - } - - private void OnUnloaded(object sender, RoutedEventArgs e) - { - this.UnSubscribeEvents(); - } - - private void SubscribeEvents() - { - // Always unsubscribe events to ensure we don't subscribe twice - this.UnSubscribeEvents(); - - this.UpdateScalableControlSubscribing(); - - if (this.LauncherButton != null) - { - this.LauncherButton.Click += this.OnDialogLauncherButtonClick; - } - - if (this.DropDownPopup != null) - { - this.DropDownPopup.Opened += this.OnPopupOpened; - this.DropDownPopup.Closed += this.OnPopupClosed; - } - } - - private void UnSubscribeEvents() - { - this.UpdateScalableControlSubscribing(false); - - if (this.LauncherButton != null) - { - this.LauncherButton.Click -= this.OnDialogLauncherButtonClick; - } - - if (this.DropDownPopup != null) - { - this.DropDownPopup.Opened -= this.OnPopupOpened; - this.DropDownPopup.Closed -= this.OnPopupClosed; - } - } - - #endregion - - #region Methods - - /// - /// Gets a panel with items - /// - /// - internal Panel GetPanel() { return this.upPanel; } - - /// - /// Gets cmmon layout root for popup and groupbox - /// - /// - internal Panel GetLayoutRoot() { return this.parentPanel; } - - #endregion - - #region Snapping - - /// - /// Snaps / Unsnaps the Visual - /// (remove visuals and substitute with freezed image) - /// - public bool IsSnapped - { - get - { - return this.isSnapped; - } - set - { - if (value == this.isSnapped) - { - return; - } - - if (value) - { - if (this.IsVisible) - { - // Render the freezed image - var renderTargetBitmap = new RenderTargetBitmap((int)this.ActualWidth, (int)this.ActualHeight, 96, 96, PixelFormats.Pbgra32); - renderTargetBitmap.Render((Visual)VisualTreeHelper.GetChild(this, 0)); - this.snappedImage.FlowDirection = this.FlowDirection; - this.snappedImage.Source = renderTargetBitmap; - this.snappedImage.Width = this.ActualWidth; - this.snappedImage.Height = this.ActualHeight; - this.snappedImage.Visibility = Visibility.Visible; - this.isSnapped = true; - } - } - else if (this.snappedImage != null) - { - // Clean up - this.snappedImage.Visibility = Visibility.Collapsed; - this.isSnapped = false; - } - - this.InvalidateVisual(); - } - } - - #endregion - - #region Caching - - // Pair of chached states - struct StateScale - { - public RibbonGroupBoxState State; - public int Scale; - } - - // Cache - readonly Dictionary cachedMeasures = new Dictionary(); - - /// - /// Gets or sets intermediate state of the group box - /// - internal RibbonGroupBoxState StateIntermediate - { - get; - set; - } - - /// - /// Gets or sets intermediate scale of the group box - /// - internal int ScaleIntermediate - { - get; - set; - } - - /// - /// Gets intermediate desired size - /// - internal Size DesiredSizeIntermediate - { - get - { - Size result; - var stateScale = this.GetCurrentIntermediateStateScale(); - - if (this.cachedMeasures.TryGetValue(stateScale, out result) == false) - { - this.SuppressCacheReseting = true; - this.UpdateScalableControlSubscribing(); - - // Get desired size for these values - var backupState = this.State; - var backupScale = this.Scale; - this.State = this.StateIntermediate; - this.Scale = this.ScaleIntermediate; - this.InvalidateLayout(); - this.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); - this.cachedMeasures.Add(stateScale, this.DesiredSize); - result = this.DesiredSize; - - // Rollback changes - this.State = backupState; - this.Scale = backupScale; - this.InvalidateLayout(); - this.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); - - this.SuppressCacheReseting = false; - } - - return result; - } - } - - /// - /// Clears cache - /// - public void ClearCache() - { - this.cachedMeasures.Clear(); - } - - /// - /// Invalidates layout (with children) - /// - internal void InvalidateLayout() - { - InvalidateMeasureRecursive(this); - } - - private static void InvalidateMeasureRecursive(UIElement element) - { - if (element == null) - { - return; - } - - element.InvalidateMeasure(); - - for (var i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++) - { - var child = VisualTreeHelper.GetChild(element, i) as UIElement; - - if (child == null) - { - continue; - } - - InvalidateMeasureRecursive(child); - } - } - - #endregion - - #region Overrides - - /// - /// When overridden in a derived class, is invoked whenever application code - /// or internal processes call System.Windows.FrameworkElement.ApplyTemplate(). - /// - public override void OnApplyTemplate() - { - this.UnSubscribeEvents(); - - // Clear cache - this.cachedMeasures.Clear(); - - this.LauncherButton = this.GetTemplateChild("PART_DialogLauncherButton") as Button; - - if (this.LauncherButton != null) - { - if (this.LauncherKeys != null) - { - this.LauncherButton.KeyTip = this.LauncherKeys; - } - } - - this.DropDownPopup = this.GetTemplateChild("PART_Popup") as Popup; - - this.upPanel = this.GetTemplateChild("PART_UpPanel") as Panel; - this.parentPanel = this.GetTemplateChild("PART_ParentPanel") as Panel; - - this.snappedImage = this.GetTemplateChild("PART_SnappedImage") as Image; - - this.SubscribeEvents(); - } - - private void OnPopupOpened(object sender, EventArgs e) - { - if (this.DropDownOpened != null) this.DropDownOpened(this, e); - } - - private void OnPopupClosed(object sender, EventArgs e) - { - if (this.DropDownClosed != null) this.DropDownClosed(this, e); - } - - /// - /// Invoked when an unhandled System.Windows.UIElement.PreviewMouseLeftButtonDown - /// event reaches an element in its route that is derived from this class. - /// Implement this method to add class handling for this event. - /// - /// The System.Windows.Input.MouseButtonEventArgs that contains the event data. - /// The event data reports that the left mouse button was pressed. - protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) - { - if (e.Source != this - || this.DropDownPopup == null) - { - return; - } - - if (this.State == RibbonGroupBoxState.Collapsed - || this.State == RibbonGroupBoxState.QuickAccess) - { - e.Handled = true; - - if (!this.IsDropDownOpen) - { - this.IsDropDownOpen = true; - } - else - { - PopupService.RaiseDismissPopupEventAsync(this, DismissPopupMode.MouseNotOver); - } - } - } - - /// - /// Supports layout behavior when a child element is resized. - /// - /// The child element that is being resized. - protected override void OnChildDesiredSizeChanged(UIElement child) - { - base.OnChildDesiredSizeChanged(child); - - this.cachedMeasures.Remove(this.GetCurrentIntermediateStateScale()); - } - - private StateScale GetCurrentIntermediateStateScale() - { - var stateScale = new StateScale - { - Scale = this.ScaleIntermediate, - State = this.StateIntermediate - }; - return stateScale; - } - - #endregion - - #region Event Handling - - /// - /// Dialog launcher button click handler - /// - /// Sender - /// the event data - private void OnDialogLauncherButtonClick(object sender, RoutedEventArgs e) - { - if (this.LauncherClick != null) - { - this.LauncherClick(this, e); - } - } - - // Handles popup closing - private void OnRibbonGroupBoxPopupClosing() - { - //IsHitTestVisible = true; - if (Mouse.Captured == this) - { - Mouse.Capture(null); - } - } - - // handles popup opening - private void OnRibbonGroupBoxPopupOpening() - { - //IsHitTestVisible = false; - Mouse.Capture(this, CaptureMode.SubTree); - } - - /// - /// Handles IsOpen propertyu changes - /// - /// Object - /// The event data - private static void OnIsOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var ribbon = (RibbonGroupBox)d; - - if (ribbon.IsDropDownOpen) - { - ribbon.OnRibbonGroupBoxPopupOpening(); - } - else - { - ribbon.OnRibbonGroupBoxPopupClosing(); - } - } - - #endregion - - #region Quick Access Item Creating - - /// - /// Gets control which represents shortcut item. - /// This item MUST be syncronized with the original - /// and send command to original one control. - /// - /// Control which represents shortcut item - public FrameworkElement CreateQuickAccessItem() - { - var groupBox = new RibbonGroupBox(); - - groupBox.DropDownOpened += this.OnQuickAccessOpened; - groupBox.DropDownClosed += this.OnQuickAccessClosed; - - groupBox.State = RibbonGroupBoxState.QuickAccess; - - RibbonControl.Bind(this, groupBox, "ItemTemplateSelector", ItemTemplateSelectorProperty, BindingMode.OneWay); - RibbonControl.Bind(this, groupBox, "ItemTemplate", ItemTemplateProperty, BindingMode.OneWay); - RibbonControl.Bind(this, groupBox, "ItemsSource", ItemsSourceProperty, BindingMode.OneWay); - RibbonControl.Bind(this, groupBox, "LauncherCommandParameter", LauncherCommandParameterProperty, BindingMode.OneWay); - RibbonControl.Bind(this, groupBox, "LauncherCommand", LauncherCommandProperty, BindingMode.OneWay); - RibbonControl.Bind(this, groupBox, "LauncherCommandTarget", LauncherCommandTargetProperty, BindingMode.OneWay); - RibbonControl.Bind(this, groupBox, "LauncherIcon", LauncherIconProperty, BindingMode.OneWay); - RibbonControl.Bind(this, groupBox, "LauncherText", LauncherTextProperty, BindingMode.OneWay); - RibbonControl.Bind(this, groupBox, "LauncherToolTip", LauncherToolTipProperty, BindingMode.OneWay); - RibbonControl.Bind(this, groupBox, "IsLauncherEnabled", IsLauncherEnabledProperty, BindingMode.OneWay); - RibbonControl.Bind(this, groupBox, "IsLauncherVisible", IsLauncherVisibleProperty, BindingMode.OneWay); - RibbonControl.Bind(this, groupBox, "DialogLauncherButtonKeyTipKeys", DialogLauncherButtonKeyTipKeysProperty, BindingMode.OneWay); - groupBox.LauncherClick += this.LauncherClick; - - if (this.Icon != null) - { - var iconVisual = this.Icon as Visual; - if (iconVisual != null) - { - var rect = new Rectangle - { - Width = 16, - Height = 16, - Fill = new VisualBrush(iconVisual) - }; - groupBox.Icon = rect; - } - else - { - RibbonControl.Bind(this, groupBox, "Icon", RibbonControl.IconProperty, BindingMode.OneWay); - } - } - - if (this.Header != null) - { - RibbonControl.Bind(this, groupBox, "Header", RibbonControl.HeaderProperty, BindingMode.OneWay); - } - - return groupBox; - } - - private void OnQuickAccessOpened(object sender, EventArgs e) - { - if (!this.IsDropDownOpen - && !this.IsSnapped) - { - var groupBox = sender as RibbonGroupBox; - // Save state - this.IsSnapped = true; - - if (this.ItemsSource == null) - { - for (var i = 0; i < this.Items.Count; i++) - { - var item = this.Items[0]; - this.Items.Remove(item); - groupBox.Items.Add(item); - i--; - } - } - } - } - - private void OnQuickAccessClosed(object sender, EventArgs e) - { - var groupBox = sender as RibbonGroupBox; - - if (this.ItemsSource == null) - { - for (var i = 0; i < groupBox.Items.Count; i++) - { - var item = groupBox.Items[0]; - groupBox.Items.Remove(item); - this.Items.Add(item); - i--; - } - } - - this.IsSnapped = false; - } - - /// - /// Gets or sets whether control can be added to quick access toolbar - /// - public bool CanAddToQuickAccessToolBar - { - get { return (bool)this.GetValue(CanAddToQuickAccessToolBarProperty); } - set { this.SetValue(CanAddToQuickAccessToolBarProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for CanAddToQuickAccessToolBar. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty CanAddToQuickAccessToolBarProperty = - DependencyProperty.Register("CanAddToQuickAccessToolBar", typeof(bool), typeof(RibbonGroupBox), new UIPropertyMetadata(true, RibbonControl.OnCanAddToQuickAccessToolbarChanged)); - - #endregion - - #region Implementation of IKeyTipedControl - - /// - /// Handles key tip pressed - /// - public void OnKeyTipPressed() - { - if (this.State == RibbonGroupBoxState.Collapsed - || this.State == RibbonGroupBoxState.QuickAccess) - { - this.IsDropDownOpen = true; - } - } - - /// - /// Handles back navigation with KeyTips - /// - public void OnKeyTipBack() - { - this.IsDropDownOpen = false; - } - - #endregion - } +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Data; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; + +namespace Fluent +{ + using Fluent.Internal; + + /// + /// RibbonGroup represents a logical group of controls as they appear on + /// a RibbonTab. These groups can resize its content + /// + [TemplatePart(Name = "PART_DialogLauncherButton", Type = typeof(Button))] + [TemplatePart(Name = "PART_Popup", Type = typeof(Popup))] + [TemplatePart(Name = "PART_UpPanel", Type = typeof(Panel))] + public class RibbonGroupBox : ItemsControl, IQuickAccessItemProvider, IDropDownControl, IKeyTipedControl, IHeaderedControl + { + #region Fields + + // up part + private Panel upPanel; + + private Panel parentPanel; + + // Freezed image (created during snapping) + private Image snappedImage; + + // Is visual currently snapped + private bool isSnapped; + + private readonly ItemContainerGeneratorAction updateChildSizesItemContainerGeneratorAction; + + #endregion + + #region Properties + + #region KeyTip + + /// + /// Gets or sets KeyTip for element. + /// + public string KeyTip + { + get { return (string)this.GetValue(KeyTipProperty); } + set { this.SetValue(KeyTipProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Keys. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty KeyTipProperty = Fluent.KeyTip.KeysProperty.AddOwner(typeof(RibbonGroupBox)); + + #endregion + + /// + /// Gets drop down popup + /// + public Popup DropDownPopup { get; private set; } + + /// + /// Gets a value indicating whether context menu is opened + /// + public bool IsContextMenuOpened { get; set; } + + #region State + + /// + /// Gets or sets the current state of the group + /// + public RibbonGroupBoxState State + { + get { return (RibbonGroupBoxState)this.GetValue(StateProperty); } + set { this.SetValue(StateProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for State. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty StateProperty = + DependencyProperty.Register("State", typeof(RibbonGroupBoxState), typeof(RibbonGroupBox), new UIPropertyMetadata(RibbonGroupBoxState.Large, StatePropertyChanged)); + + /// + /// On state property changed + /// + /// Object + /// The event data + static void StatePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var ribbonGroupBox = (RibbonGroupBox)d; + ribbonGroupBox.updateChildSizesItemContainerGeneratorAction.QueueAction(); + } + + private void UpdateChildSizes() + { + var groupBoxState = this.State == RibbonGroupBoxState.QuickAccess + ? RibbonGroupBoxState.Collapsed + : this.State; + + foreach (var item in this.Items) + { + var element = this.ItemContainerGenerator.ContainerFromItem(item); + + if (element == null) + { + continue; + } + + RibbonProperties.SetAppropriateSize(element, groupBoxState); + } + } + + #endregion + + #region Scale + + // Current scale index + private int scale; + + /// + /// Gets or sets scale index (for internal IRibbonScalableControl) + /// + internal int Scale + { + get { return this.scale; } + set + { + var difference = value - this.scale; + this.scale = value; + + for (var i = 0; i < Math.Abs(difference); i++) + { + if (difference > 0) + { + this.IncreaseScalableElement(); + } + else + { + this.DecreaseScalableElement(); + } + } + } + } + + // Finds and increase size of all scalable elements in the given group box + private void IncreaseScalableElement() + { + foreach (var item in this.Items) + { + var scalableRibbonControl = item as IScalableRibbonControl; + if (scalableRibbonControl == null) + { + continue; + } + scalableRibbonControl.Enlarge(); + } + } + + private void OnScalableControlScaled(object sender, EventArgs e) + { + this.TryClearCache(); + } + + private void TryClearCache() + { + if (!this.SuppressCacheReseting) + { + this.cachedMeasures.Clear(); + } + } + + /// + /// Gets or sets whether to reset cache when scalable control is scaled + /// + internal bool SuppressCacheReseting { get; set; } + + // Finds and decrease size of all scalable elements in the given group box + private void DecreaseScalableElement() + { + foreach (object item in this.Items) + { + IScalableRibbonControl scalableRibbonControl = item as IScalableRibbonControl; + if (scalableRibbonControl == null) continue; + scalableRibbonControl.Reduce(); + } + } + + private void UpdateScalableControlSubscribing() + { + this.UpdateScalableControlSubscribing(true); + } + + private void UpdateScalableControlSubscribing(bool registerEvents) + { + foreach (var scalableRibbonControl in this.Items.OfType()) + { + // Always unregister first to ensure that we don't subscribe twice + scalableRibbonControl.Scaled -= this.OnScalableControlScaled; + + if (registerEvents) + { + scalableRibbonControl.Scaled += this.OnScalableControlScaled; + } + } + } + + #endregion + + #region Header + + /// + /// Gets or sets group box header + /// + public string Header + { + get { return (string)this.GetValue(HeaderProperty); } + set { this.SetValue(HeaderProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Header. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty HeaderProperty = + DependencyProperty.Register("Header", typeof(string), typeof(RibbonGroupBox), new UIPropertyMetadata()); + + object IHeaderedControl.Header + { + get { return this.Header; } + + set { this.Header = (string)value; } + } + + #endregion + + #region IsLauncherVisible + + /// + /// Gets or sets dialog launcher button visibility + /// + public bool IsLauncherVisible + { + get { return (bool)this.GetValue(IsLauncherVisibleProperty); } + set { this.SetValue(IsLauncherVisibleProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for IsLauncherVisible. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IsLauncherVisibleProperty = + DependencyProperty.Register("IsLauncherVisible", typeof(bool), typeof(RibbonGroupBox), new UIPropertyMetadata(false)); + + #endregion + + #region LauncherKeys + + /// + /// Gets or sets key tip for dialog launcher button + /// + [DisplayName("DialogLauncher Keys"), + Category("KeyTips"), + Description("Key tip keys for dialog launcher button")] + public string LauncherKeys + { + get { return (string)this.GetValue(DialogLauncherButtonKeyTipKeysProperty); } + set { this.SetValue(DialogLauncherButtonKeyTipKeysProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for + /// LauncherKeys. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty DialogLauncherButtonKeyTipKeysProperty = + DependencyProperty.Register("LauncherKeys", + typeof(string), typeof(RibbonGroupBox), new UIPropertyMetadata(null, OnDialogLauncherButtonKeyTipKeysChanged)); + + static void OnDialogLauncherButtonKeyTipKeysChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + RibbonGroupBox ribbonGroupBox = (RibbonGroupBox)d; + if (ribbonGroupBox.LauncherButton != null) + { + ribbonGroupBox.LauncherButton.KeyTip = (string)e.NewValue; + } + } + + #endregion + + #region LauncherIcon + + /// + /// Gets or sets launcher button icon + /// + public object LauncherIcon + { + get { return (ImageSource)this.GetValue(LauncherIconProperty); } + set { this.SetValue(LauncherIconProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for LauncherIcon. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty LauncherIconProperty = + DependencyProperty.Register("LauncherIcon", typeof(object), typeof(RibbonGroupBox), new UIPropertyMetadata(null, OnIconChanged)); + + #endregion + + #region LauncherIcon + + /// + /// Gets or sets launcher button text + /// + public string LauncherText + { + get { return (string)this.GetValue(LauncherTextProperty); } + set { this.SetValue(LauncherTextProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for LauncherIcon. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty LauncherTextProperty = + DependencyProperty.Register("LauncherText", typeof(string), typeof(RibbonGroupBox), new UIPropertyMetadata(null)); + + #endregion + + #region LauncherCommand + + /// + /// Gets or sets the command to invoke when this button is pressed. This is a dependency property. + /// + [Category("Action"), Localizability(LocalizationCategory.NeverLocalize), Bindable(true)] + public ICommand LauncherCommand + { + get + { + return (ICommand)this.GetValue(LauncherCommandProperty); + } + set + { + this.SetValue(LauncherCommandProperty, value); + } + } + + /// + /// Gets or sets the parameter to pass to the System.Windows.Controls.Primitives.ButtonBase.Command property. This is a dependency property. + /// + [Bindable(true), Localizability(LocalizationCategory.NeverLocalize), Category("Action")] + public object LauncherCommandParameter + { + get + { + return this.GetValue(LauncherCommandParameterProperty); + } + set + { + this.SetValue(LauncherCommandParameterProperty, value); + } + } + + /// + /// Gets or sets the element on which to raise the specified command. This is a dependency property. + /// + [Bindable(true), Category("Action")] + public IInputElement LauncherCommandTarget + { + get + { + return (IInputElement)this.GetValue(LauncherCommandTargetProperty); + } + set + { + this.SetValue(LauncherCommandTargetProperty, value); + } + } + + /// + /// Identifies the System.Windows.Controls.Primitives.ButtonBase.CommandParameter dependency property. + /// + public static readonly DependencyProperty LauncherCommandParameterProperty = DependencyProperty.Register("LauncherCommandParameter", typeof(object), typeof(RibbonGroupBox), new FrameworkPropertyMetadata(null)); + /// + /// Identifies the routed System.Windows.Controls.Primitives.ButtonBase.Command dependency property. + /// + public static readonly DependencyProperty LauncherCommandProperty = DependencyProperty.Register("LauncherCommand", typeof(ICommand), typeof(RibbonGroupBox), new FrameworkPropertyMetadata(null)); + + /// + /// Identifies the System.Windows.Controls.Primitives.ButtonBase.CommandTarget dependency property. + /// + public static readonly DependencyProperty LauncherCommandTargetProperty = DependencyProperty.Register("LauncherCommandTarget", typeof(IInputElement), typeof(RibbonGroupBox), new FrameworkPropertyMetadata(null)); + + #endregion + + #region LauncherToolTip + + /// + /// Gets or sets launcher button tooltip + /// + public object LauncherToolTip + { + get { return (object)this.GetValue(LauncherToolTipProperty); } + set { this.SetValue(LauncherToolTipProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for LauncherToolTip. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty LauncherToolTipProperty = + DependencyProperty.Register("LauncherToolTip", typeof(object), typeof(RibbonGroupBox), new UIPropertyMetadata(null)); + + + + #endregion + + #region IsLauncherEnabled + + /// + /// Gets or sets whether launcher button is enabled + /// + public bool IsLauncherEnabled + { + get { return (bool)this.GetValue(IsLauncherEnabledProperty); } + set { this.SetValue(IsLauncherEnabledProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for IsLauncherEnabled. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IsLauncherEnabledProperty = + DependencyProperty.Register("IsLauncherEnabled", typeof(bool), typeof(RibbonGroupBox), new UIPropertyMetadata(true)); + + #endregion + + #region LauncherButton + + /// + /// Gets launcher button + /// + public Button LauncherButton + { + get { return (Button)this.GetValue(LauncherButtonProperty); } + private set { this.SetValue(LauncherButtonPropertyKey, value); } + } + + private static readonly DependencyPropertyKey LauncherButtonPropertyKey = + DependencyProperty.RegisterReadOnly("LauncherButton", typeof(Button), typeof(RibbonGroupBox), new UIPropertyMetadata(null)); + + /// + /// Using a DependencyProperty as the backing store for LauncherButton. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty LauncherButtonProperty = LauncherButtonPropertyKey.DependencyProperty; + + #endregion + + #region IsOpen + + /// + /// Gets or sets drop down popup visibility + /// + public bool IsDropDownOpen + { + get { return (bool)this.GetValue(IsDropDownOpenProperty); } + set { this.SetValue(IsDropDownOpenProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for IsOpen. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IsDropDownOpenProperty = DependencyProperty.Register("IsDropDownOpen", typeof(bool), typeof(RibbonGroupBox), new UIPropertyMetadata(false, OnIsOpenChanged, CoerceIsDropDownOpen)); + + private static object CoerceIsDropDownOpen(DependencyObject d, object basevalue) + { + RibbonGroupBox box = d as RibbonGroupBox; + if ((box.State != RibbonGroupBoxState.Collapsed) && (box.State != RibbonGroupBoxState.QuickAccess)) return false; + return basevalue; + } + + #endregion + + #region LogicalChildren + + /// + /// Gets an enumerator for the logical child objects of + /// the System.Windows.Controls.ItemsControl object. + /// + protected override IEnumerator LogicalChildren + { + get + { + foreach (var item in this.Items) + { + yield return item; + } + + if (this.LauncherButton != null) + { + yield return this.LauncherButton; + } + } + } + + #endregion + + #region Icon + + /// + /// Gets or sets icon + /// + public object Icon + //public ImageSource Icon + { + get { return this.GetValue(IconProperty); } + set { this.SetValue(IconProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Icon. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IconProperty = + RibbonControl.IconProperty.AddOwner(typeof(RibbonGroupBox), new UIPropertyMetadata(null, OnIconChanged)); + + + private static void OnIconChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + RibbonGroupBox element = d as RibbonGroupBox; + FrameworkElement oldElement = e.OldValue as FrameworkElement; + if (oldElement != null) element.RemoveLogicalChild(oldElement); + FrameworkElement newElement = e.NewValue as FrameworkElement; + if (newElement != null) element.AddLogicalChild(newElement); + } + + #endregion + + #endregion + + #region Events + + /// + /// Dialog launcher btton click event + /// + public event RoutedEventHandler LauncherClick; + + /// + /// Occurs when context menu is opened + /// + public event EventHandler DropDownOpened; + + /// + /// Occurs when context menu is closed + /// + public event EventHandler DropDownClosed; + + #endregion + + #region Initialize + + /// + /// Static constructor + /// + [SuppressMessage("Microsoft.Performance", "CA1810")] + static RibbonGroupBox() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(RibbonGroupBox), new FrameworkPropertyMetadata(typeof(RibbonGroupBox))); + VisibilityProperty.AddOwner(typeof(RibbonGroupBox), new FrameworkPropertyMetadata(OnVisibilityChanged)); + + PopupService.Attach(typeof(RibbonGroupBox)); + StyleProperty.OverrideMetadata(typeof(RibbonGroupBox), new FrameworkPropertyMetadata(null, OnCoerceStyle)); + + ContextMenuService.Attach(typeof(RibbonGroupBox)); + } + + // Coerce object style + private static object OnCoerceStyle(DependencyObject d, object basevalue) + { + if (basevalue == null) + { + basevalue = (d as FrameworkElement).TryFindResource(typeof(RibbonGroupBox)); + } + + return basevalue; + } + + // Handles visibility changed + private static void OnVisibilityChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var box = (d as RibbonGroupBox); + if (box != null) box.ClearCache(); + } + + /// + /// Default constructor + /// + public RibbonGroupBox() + { + this.ToolTip = new ToolTip(); + ((ToolTip)this.ToolTip).Template = null; + this.CoerceValue(ContextMenuProperty); + this.Focusable = false; + + this.Loaded += this.OnLoaded; + this.Unloaded += this.OnUnloaded; + + this.updateChildSizesItemContainerGeneratorAction = new ItemContainerGeneratorAction(this.ItemContainerGenerator, this.UpdateChildSizes); + } + + private void OnLoaded(object sender, RoutedEventArgs e) + { + this.SubscribeEvents(); + } + + private void OnUnloaded(object sender, RoutedEventArgs e) + { + this.UnSubscribeEvents(); + } + + private void SubscribeEvents() + { + // Always unsubscribe events to ensure we don't subscribe twice + this.UnSubscribeEvents(); + + this.UpdateScalableControlSubscribing(); + + if (this.LauncherButton != null) + { + this.LauncherButton.Click += this.OnDialogLauncherButtonClick; + } + + if (this.DropDownPopup != null) + { + this.DropDownPopup.Opened += this.OnPopupOpened; + this.DropDownPopup.Closed += this.OnPopupClosed; + } + } + + private void UnSubscribeEvents() + { + this.UpdateScalableControlSubscribing(false); + + if (this.LauncherButton != null) + { + this.LauncherButton.Click -= this.OnDialogLauncherButtonClick; + } + + if (this.DropDownPopup != null) + { + this.DropDownPopup.Opened -= this.OnPopupOpened; + this.DropDownPopup.Closed -= this.OnPopupClosed; + } + } + + #endregion + + #region Methods + + /// + /// Gets a panel with items + /// + /// + internal Panel GetPanel() { return this.upPanel; } + + /// + /// Gets cmmon layout root for popup and groupbox + /// + /// + internal Panel GetLayoutRoot() { return this.parentPanel; } + + #endregion + + #region Snapping + + /// + /// Snaps / Unsnaps the Visual + /// (remove visuals and substitute with freezed image) + /// + public bool IsSnapped + { + get + { + return this.isSnapped; + } + set + { + if (value == this.isSnapped) + { + return; + } + + if (value) + { + if (this.IsVisible) + { + // Render the freezed image + var renderTargetBitmap = new RenderTargetBitmap((int)this.ActualWidth, (int)this.ActualHeight, 96, 96, PixelFormats.Pbgra32); + renderTargetBitmap.Render((Visual)VisualTreeHelper.GetChild(this, 0)); + this.snappedImage.FlowDirection = this.FlowDirection; + this.snappedImage.Source = renderTargetBitmap; + this.snappedImage.Width = this.ActualWidth; + this.snappedImage.Height = this.ActualHeight; + this.snappedImage.Visibility = Visibility.Visible; + this.isSnapped = true; + } + } + else if (this.snappedImage != null) + { + // Clean up + this.snappedImage.Visibility = Visibility.Collapsed; + this.isSnapped = false; + } + + this.InvalidateVisual(); + } + } + + #endregion + + #region Caching + + // Pair of chached states + struct StateScale + { + public RibbonGroupBoxState State; + public int Scale; + } + + // Cache + readonly Dictionary cachedMeasures = new Dictionary(); + + /// + /// Gets or sets intermediate state of the group box + /// + internal RibbonGroupBoxState StateIntermediate + { + get; + set; + } + + /// + /// Gets or sets intermediate scale of the group box + /// + internal int ScaleIntermediate + { + get; + set; + } + + /// + /// Gets intermediate desired size + /// + internal Size DesiredSizeIntermediate + { + get + { + Size result; + var stateScale = this.GetCurrentIntermediateStateScale(); + + if (this.cachedMeasures.TryGetValue(stateScale, out result) == false) + { + this.SuppressCacheReseting = true; + this.UpdateScalableControlSubscribing(); + + // Get desired size for these values + var backupState = this.State; + var backupScale = this.Scale; + this.State = this.StateIntermediate; + this.Scale = this.ScaleIntermediate; + this.InvalidateLayout(); + this.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); + this.cachedMeasures.Add(stateScale, this.DesiredSize); + result = this.DesiredSize; + + // Rollback changes + this.State = backupState; + this.Scale = backupScale; + this.InvalidateLayout(); + this.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); + + this.SuppressCacheReseting = false; + } + + return result; + } + } + + /// + /// Clears cache + /// + public void ClearCache() + { + this.cachedMeasures.Clear(); + } + + /// + /// Invalidates layout (with children) + /// + internal void InvalidateLayout() + { + InvalidateMeasureRecursive(this); + } + + private static void InvalidateMeasureRecursive(UIElement element) + { + if (element == null) + { + return; + } + + element.InvalidateMeasure(); + + for (var i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++) + { + var child = VisualTreeHelper.GetChild(element, i) as UIElement; + + if (child == null) + { + continue; + } + + InvalidateMeasureRecursive(child); + } + } + + #endregion + + #region Overrides + + /// + /// When overridden in a derived class, is invoked whenever application code + /// or internal processes call System.Windows.FrameworkElement.ApplyTemplate(). + /// + public override void OnApplyTemplate() + { + this.UnSubscribeEvents(); + + // Clear cache + this.cachedMeasures.Clear(); + + this.LauncherButton = this.GetTemplateChild("PART_DialogLauncherButton") as Button; + + if (this.LauncherButton != null) + { + if (this.LauncherKeys != null) + { + this.LauncherButton.KeyTip = this.LauncherKeys; + } + } + + this.DropDownPopup = this.GetTemplateChild("PART_Popup") as Popup; + + this.upPanel = this.GetTemplateChild("PART_UpPanel") as Panel; + this.parentPanel = this.GetTemplateChild("PART_ParentPanel") as Panel; + + this.snappedImage = this.GetTemplateChild("PART_SnappedImage") as Image; + + this.SubscribeEvents(); + } + + private void OnPopupOpened(object sender, EventArgs e) + { + if (this.DropDownOpened != null) this.DropDownOpened(this, e); + } + + private void OnPopupClosed(object sender, EventArgs e) + { + if (this.DropDownClosed != null) this.DropDownClosed(this, e); + } + + /// + /// Invoked when an unhandled System.Windows.UIElement.PreviewMouseLeftButtonDown + /// event reaches an element in its route that is derived from this class. + /// Implement this method to add class handling for this event. + /// + /// The System.Windows.Input.MouseButtonEventArgs that contains the event data. + /// The event data reports that the left mouse button was pressed. + protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) + { + if (e.Source != this + || this.DropDownPopup == null) + { + return; + } + + if (this.State == RibbonGroupBoxState.Collapsed + || this.State == RibbonGroupBoxState.QuickAccess) + { + e.Handled = true; + + if (!this.IsDropDownOpen) + { + this.IsDropDownOpen = true; + } + else + { + PopupService.RaiseDismissPopupEventAsync(this, DismissPopupMode.MouseNotOver); + } + } + } + + /// + /// Supports layout behavior when a child element is resized. + /// + /// The child element that is being resized. + protected override void OnChildDesiredSizeChanged(UIElement child) + { + base.OnChildDesiredSizeChanged(child); + + this.cachedMeasures.Remove(this.GetCurrentIntermediateStateScale()); + } + + private StateScale GetCurrentIntermediateStateScale() + { + var stateScale = new StateScale + { + Scale = this.ScaleIntermediate, + State = this.StateIntermediate + }; + return stateScale; + } + + #endregion + + #region Event Handling + + /// + /// Dialog launcher button click handler + /// + /// Sender + /// the event data + private void OnDialogLauncherButtonClick(object sender, RoutedEventArgs e) + { + if (this.LauncherClick != null) + { + this.LauncherClick(this, e); + } + } + + // Handles popup closing + private void OnRibbonGroupBoxPopupClosing() + { + //IsHitTestVisible = true; + if (Mouse.Captured == this) + { + Mouse.Capture(null); + } + } + + // handles popup opening + private void OnRibbonGroupBoxPopupOpening() + { + //IsHitTestVisible = false; + Mouse.Capture(this, CaptureMode.SubTree); + } + + /// + /// Handles IsOpen propertyu changes + /// + /// Object + /// The event data + private static void OnIsOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var ribbon = (RibbonGroupBox)d; + + if (ribbon.IsDropDownOpen) + { + ribbon.OnRibbonGroupBoxPopupOpening(); + } + else + { + ribbon.OnRibbonGroupBoxPopupClosing(); + } + } + + #endregion + + #region Quick Access Item Creating + + /// + /// Gets control which represents shortcut item. + /// This item MUST be syncronized with the original + /// and send command to original one control. + /// + /// Control which represents shortcut item + public FrameworkElement CreateQuickAccessItem() + { + var groupBox = new RibbonGroupBox(); + + groupBox.DropDownOpened += this.OnQuickAccessOpened; + groupBox.DropDownClosed += this.OnQuickAccessClosed; + + groupBox.State = RibbonGroupBoxState.QuickAccess; + + RibbonControl.Bind(this, groupBox, "ItemTemplateSelector", ItemTemplateSelectorProperty, BindingMode.OneWay); + RibbonControl.Bind(this, groupBox, "ItemTemplate", ItemTemplateProperty, BindingMode.OneWay); + RibbonControl.Bind(this, groupBox, "ItemsSource", ItemsSourceProperty, BindingMode.OneWay); + RibbonControl.Bind(this, groupBox, "LauncherCommandParameter", LauncherCommandParameterProperty, BindingMode.OneWay); + RibbonControl.Bind(this, groupBox, "LauncherCommand", LauncherCommandProperty, BindingMode.OneWay); + RibbonControl.Bind(this, groupBox, "LauncherCommandTarget", LauncherCommandTargetProperty, BindingMode.OneWay); + RibbonControl.Bind(this, groupBox, "LauncherIcon", LauncherIconProperty, BindingMode.OneWay); + RibbonControl.Bind(this, groupBox, "LauncherText", LauncherTextProperty, BindingMode.OneWay); + RibbonControl.Bind(this, groupBox, "LauncherToolTip", LauncherToolTipProperty, BindingMode.OneWay); + RibbonControl.Bind(this, groupBox, "IsLauncherEnabled", IsLauncherEnabledProperty, BindingMode.OneWay); + RibbonControl.Bind(this, groupBox, "IsLauncherVisible", IsLauncherVisibleProperty, BindingMode.OneWay); + RibbonControl.Bind(this, groupBox, "DialogLauncherButtonKeyTipKeys", DialogLauncherButtonKeyTipKeysProperty, BindingMode.OneWay); + groupBox.LauncherClick += this.LauncherClick; + + if (this.Icon != null) + { + var iconVisual = this.Icon as Visual; + if (iconVisual != null) + { + var rect = new Rectangle + { + Width = 16, + Height = 16, + Fill = new VisualBrush(iconVisual) + }; + groupBox.Icon = rect; + } + else + { + RibbonControl.Bind(this, groupBox, "Icon", RibbonControl.IconProperty, BindingMode.OneWay); + } + } + + if (this.Header != null) + { + RibbonControl.Bind(this, groupBox, "Header", RibbonControl.HeaderProperty, BindingMode.OneWay); + } + + return groupBox; + } + + private void OnQuickAccessOpened(object sender, EventArgs e) + { + if (!this.IsDropDownOpen + && !this.IsSnapped) + { + var groupBox = sender as RibbonGroupBox; + // Save state + this.IsSnapped = true; + + if (this.ItemsSource == null) + { + for (var i = 0; i < this.Items.Count; i++) + { + var item = this.Items[0]; + this.Items.Remove(item); + groupBox.Items.Add(item); + i--; + } + } + } + } + + private void OnQuickAccessClosed(object sender, EventArgs e) + { + var groupBox = sender as RibbonGroupBox; + + if (this.ItemsSource == null) + { + for (var i = 0; i < groupBox.Items.Count; i++) + { + var item = groupBox.Items[0]; + groupBox.Items.Remove(item); + this.Items.Add(item); + i--; + } + } + + this.IsSnapped = false; + } + + /// + /// Gets or sets whether control can be added to quick access toolbar + /// + public bool CanAddToQuickAccessToolBar + { + get { return (bool)this.GetValue(CanAddToQuickAccessToolBarProperty); } + set { this.SetValue(CanAddToQuickAccessToolBarProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for CanAddToQuickAccessToolBar. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty CanAddToQuickAccessToolBarProperty = + DependencyProperty.Register("CanAddToQuickAccessToolBar", typeof(bool), typeof(RibbonGroupBox), new UIPropertyMetadata(true, RibbonControl.OnCanAddToQuickAccessToolbarChanged)); + + #endregion + + #region Implementation of IKeyTipedControl + + /// + /// Handles key tip pressed + /// + public void OnKeyTipPressed() + { + if (this.State == RibbonGroupBoxState.Collapsed + || this.State == RibbonGroupBoxState.QuickAccess) + { + this.IsDropDownOpen = true; + } + } + + /// + /// Handles back navigation with KeyTips + /// + public void OnKeyTipBack() + { + this.IsDropDownOpen = false; + } + + #endregion + } } \ No newline at end of file diff --git a/Fluent/Controls/RibbonGroupsContainer.cs b/Fluent.Ribbon/Controls/RibbonGroupsContainer.cs similarity index 97% rename from Fluent/Controls/RibbonGroupsContainer.cs rename to Fluent.Ribbon/Controls/RibbonGroupsContainer.cs index 50371f3e5..ee578e6c4 100644 --- a/Fluent/Controls/RibbonGroupsContainer.cs +++ b/Fluent.Ribbon/Controls/RibbonGroupsContainer.cs @@ -1,599 +1,599 @@ -using System; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Controls.Primitives; -using System.Windows.Media; - -namespace Fluent -{ - using Fluent.Internal; - - /// - /// Represent panel with ribbon group. - /// It is automatically adjusting size of controls - /// - public class RibbonGroupsContainer : Panel, IScrollInfo - { - #region Reduce Order - - /// - /// Gets or sets reduce order of group in the ribbon panel. - /// It must be enumerated with comma from the first to reduce to - /// the last to reduce (use Control.Name as group name in the enum). - /// Enclose in parentheses as (Control.Name) to reduce/enlarge - /// scalable elements in the given group - /// - public string ReduceOrder - { - get { return (string)this.GetValue(ReduceOrderProperty); } - set { this.SetValue(ReduceOrderProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for ReduceOrder. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty ReduceOrderProperty = - DependencyProperty.Register("ReduceOrder", typeof(string), typeof(RibbonGroupsContainer), new UIPropertyMetadata(ReduceOrderPropertyChanged)); - - // handles ReduseOrder property changed - static void ReduceOrderPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - RibbonGroupsContainer ribbonPanel = (RibbonGroupsContainer)d; - ribbonPanel.reduceOrder = ((string)e.NewValue).Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries); - ribbonPanel.reduceOrderIndex = ribbonPanel.reduceOrder.Length - 1; - - ribbonPanel.InvalidateMeasure(); - ribbonPanel.InvalidateArrange(); - } - - #endregion - - #region Fields - - private string[] reduceOrder = new string[0]; - private int reduceOrderIndex; - - #endregion - - #region Initialization - - /// - /// Default constructor - /// - public RibbonGroupsContainer() - : base() - { - this.Focusable = false; - } - - #endregion - - #region Layout Overridings - - /// - /// Returns a collection of the panel's UIElements. - /// - /// The logical parent of the collection to be created. - /// Returns an ordered collection of elements that have the specified logical parent. - protected override UIElementCollection CreateUIElementCollection(FrameworkElement logicalParent) - { - return new UIElementCollection(this, /*Parent as FrameworkElement*/this); - } - - /// - /// Measures all of the RibbonGroupBox, and resize them appropriately - /// to fit within the available room - /// - /// The available size that this element can give to child elements. - /// The size that the groups container determines it needs during - /// layout, based on its calculations of child element sizes. - /// - protected override Size MeasureOverride(Size availableSize) - { - var desiredSize = this.GetChildrenDesiredSizeIntermediate(); - - if (this.reduceOrder.Length == 0) - { - this.VerifyScrollData(availableSize.Width, desiredSize.Width); - return desiredSize; - } - - // If we have more available space - try to expand groups - while (desiredSize.Width <= availableSize.Width) - { - var hasMoreVariants = this.reduceOrderIndex < this.reduceOrder.Length - 1; - if (!hasMoreVariants) - { - break; - } - - // Increase size of another item - this.reduceOrderIndex++; - this.IncreaseGroupBoxSize(this.reduceOrder[this.reduceOrderIndex]); - - desiredSize = this.GetChildrenDesiredSizeIntermediate(); - } - - // If not enough space - go to next variant - while (desiredSize.Width > availableSize.Width) - { - var hasMoreVariants = this.reduceOrderIndex >= 0; - if (!hasMoreVariants) - { - break; - } - - // Decrease size of another item - this.DecreaseGroupBoxSize(this.reduceOrder[this.reduceOrderIndex]); - this.reduceOrderIndex--; - - desiredSize = this.GetChildrenDesiredSizeIntermediate(); - } - - // Set find values - foreach (var item in this.InternalChildren) - { - var groupBox = item as RibbonGroupBox; - if (groupBox == null) - { - continue; - } - - if ((groupBox.State != groupBox.StateIntermediate) || - (groupBox.Scale != groupBox.ScaleIntermediate)) - { - groupBox.SuppressCacheReseting = true; - groupBox.State = groupBox.StateIntermediate; - groupBox.Scale = groupBox.ScaleIntermediate; - groupBox.InvalidateLayout(); - groupBox.Measure(new Size(double.PositiveInfinity, availableSize.Height)); - groupBox.SuppressCacheReseting = false; - } - - // Something wrong with cache? - if (groupBox.DesiredSizeIntermediate != groupBox.DesiredSize) - { - // Reset cache and reinvoke masure - groupBox.ClearCache(); - return this.MeasureOverride(availableSize); - } - } - - this.VerifyScrollData(availableSize.Width, desiredSize.Width); - return desiredSize; - } - - private Size GetChildrenDesiredSizeIntermediate() - { - double width = 0; - double height = 0; - - foreach (UIElement child in this.InternalChildren) - { - var groupBox = child as RibbonGroupBox; - if (groupBox == null) - { - continue; - } - - var desiredSize = groupBox.DesiredSizeIntermediate; - width += desiredSize.Width; - height = Math.Max(height, desiredSize.Height); - } - - return new Size(width, height); - } - - // Increase size of the item - private void IncreaseGroupBoxSize(string name) - { - var groupBox = this.FindGroup(name); - var scale = name.StartsWith("(", StringComparison.OrdinalIgnoreCase); - if (groupBox == null) - { - return; - } - - if (scale) - { - groupBox.ScaleIntermediate++; - } - else - { - groupBox.StateIntermediate = groupBox.StateIntermediate != RibbonGroupBoxState.Large - ? groupBox.StateIntermediate - 1 - : RibbonGroupBoxState.Large; - } - } - - // Decrease size of the item - private void DecreaseGroupBoxSize(string name) - { - var groupBox = this.FindGroup(name); - var scale = name.StartsWith("(", StringComparison.OrdinalIgnoreCase); - if (groupBox == null) - { - return; - } - - if (scale) - { - groupBox.ScaleIntermediate--; - } - else - { - groupBox.StateIntermediate = groupBox.StateIntermediate != RibbonGroupBoxState.Collapsed - ? groupBox.StateIntermediate + 1 - : groupBox.StateIntermediate; - } - } - - private RibbonGroupBox FindGroup(string name) - { - if (name.StartsWith("(", StringComparison.OrdinalIgnoreCase)) - { - name = name.Substring(1, name.Length - 2); - } - - foreach (FrameworkElement child in this.InternalChildren) - { - if (child.Name == name) - { - return child as RibbonGroupBox; - } - } - return null; - } - - /// - /// When overridden in a derived class, positions child elements and determines - /// a size for a System.Windows.FrameworkElement derived class. - /// - /// The final area within the parent that this element should use to arrange itself and its children. - /// The actual size used. - protected override Size ArrangeOverride(Size finalSize) - { - var finalRect = new Rect(finalSize) - { - X = -this.HorizontalOffset - }; - foreach (UIElement item in this.InternalChildren) - { - finalRect.Width = item.DesiredSize.Width; - finalRect.Height = Math.Max(finalSize.Height, item.DesiredSize.Height); - item.Arrange(finalRect); - finalRect.X += item.DesiredSize.Width; - } - return finalSize; - } - - #endregion - - #region IScrollInfo Members - - /// - /// Gets or sets a System.Windows.Controls.ScrollViewer element that controls scrolling behavior. - /// - public ScrollViewer ScrollOwner - { - get { return this.ScrollData.ScrollOwner; } - set { this.ScrollData.ScrollOwner = value; } - } - - /// - /// Sets the amount of horizontal offset. - /// - /// The degree to which content is horizontally offset from the containing viewport. - public void SetHorizontalOffset(double offset) - { - var newValue = CoerceOffset(ValidateInputOffset(offset, "HorizontalOffset"), this.scrollData.ExtentWidth, this.scrollData.ViewportWidth); - if (DoubleUtil.AreClose(this.ScrollData.OffsetX, newValue) == false) - { - this.scrollData.OffsetX = newValue; - this.InvalidateArrange(); - } - } - /// - /// Gets the horizontal size of the extent. - /// - public double ExtentWidth - { - get { return this.ScrollData.ExtentWidth; } - } - - /// - /// Gets the horizontal offset of the scrolled content. - /// - public double HorizontalOffset - { - get { return this.ScrollData.OffsetX; } - } - - /// - /// Gets the horizontal size of the viewport for this content. - /// - public double ViewportWidth - { - get { return this.ScrollData.ViewportWidth; } - } - - /// - /// Scrolls left within content by one logical unit. - /// - public void LineLeft() - { - this.SetHorizontalOffset(this.HorizontalOffset - 16.0); - } - - /// - /// Scrolls right within content by one logical unit. - /// - public void LineRight() - { - this.SetHorizontalOffset(this.HorizontalOffset + 16.0); - } - - /// - /// Forces content to scroll until the coordinate space of a System.Windows.Media.Visual object is visible. - /// This is optimized for horizontal scrolling only - /// - /// A System.Windows.Media.Visual that becomes visible. - /// A bounding rectangle that identifies the coordinate space to make visible. - /// A System.Windows.Rect that is visible. - public Rect MakeVisible(Visual visual, Rect rectangle) - { - // We can only work on visuals that are us or children. - // An empty rect has no size or position. We can't meaningfully use it. - if (rectangle.IsEmpty - || visual == null - || ReferenceEquals(visual, this) - || !this.IsAncestorOf(visual)) - { - return Rect.Empty; - } - - // Compute the child's rect relative to (0,0) in our coordinate space. - GeneralTransform childTransform = visual.TransformToAncestor(this); - - rectangle = childTransform.TransformBounds(rectangle); - - // Initialize the viewport - Rect viewport = new Rect(this.HorizontalOffset, rectangle.Top, this.ViewportWidth, rectangle.Height); - rectangle.X += viewport.X; - - // Compute the offsets required to minimally scroll the child maximally into view. - double minX = ComputeScrollOffsetWithMinimalScroll(viewport.Left, viewport.Right, rectangle.Left, rectangle.Right); - - // We have computed the scrolling offsets; scroll to them. - this.SetHorizontalOffset(minX); - - // Compute the visible rectangle of the child relative to the viewport. - viewport.X = minX; - rectangle.Intersect(viewport); - - rectangle.X -= viewport.X; - - // Return the rectangle - return rectangle; - } - - static double ComputeScrollOffsetWithMinimalScroll( - double topView, - double bottomView, - double topChild, - double bottomChild) - { - // # CHILD POSITION CHILD SIZE SCROLL REMEDY - // 1 Above viewport <= viewport Down Align top edge of child & viewport - // 2 Above viewport > viewport Down Align bottom edge of child & viewport - // 3 Below viewport <= viewport Up Align bottom edge of child & viewport - // 4 Below viewport > viewport Up Align top edge of child & viewport - // 5 Entirely within viewport NA No scroll. - // 6 Spanning viewport NA No scroll. - // - // Note: "Above viewport" = childTop above viewportTop, childBottom above viewportBottom - // "Below viewport" = childTop below viewportTop, childBottom below viewportBottom - // These child thus may overlap with the viewport, but will scroll the same direction - /*bool fAbove = DoubleUtil.LessThan(topChild, topView) && DoubleUtil.LessThan(bottomChild, bottomView); - bool fBelow = DoubleUtil.GreaterThan(bottomChild, bottomView) && DoubleUtil.GreaterThan(topChild, topView);*/ - bool fAbove = (topChild < topView) && (bottomChild < bottomView); - bool fBelow = (bottomChild > bottomView) && (topChild > topView); - bool fLarger = (bottomChild - topChild) > (bottomView - topView); - - // Handle Cases: 1 & 4 above - if ((fAbove && !fLarger) - || (fBelow && fLarger)) - { - return topChild; - } - - // Handle Cases: 2 & 3 above - if (fAbove || fBelow) - { - return bottomChild - (bottomView - topView); - } - - // Handle cases: 5 & 6 above. - return topView; - } - - /// - /// Not implemented - /// - public void MouseWheelDown() - { - } - /// - /// Not implemented - /// - public void MouseWheelLeft() - { - } - /// - /// Not implemented - /// - public void MouseWheelRight() - { - } - /// - /// Not implemented - /// - public void MouseWheelUp() - { - } - /// - /// Not implemented - /// - public void LineDown() - { - } - /// - /// Not implemented - /// - public void LineUp() - { - } - /// - /// Not implemented - /// - public void PageDown() - { - } - /// - /// Not implemented - /// - public void PageLeft() - { - } - /// - /// Not implemented - /// - public void PageRight() - { - } - /// - /// Not implemented - /// - public void PageUp() - { - } - /// - /// Not implemented - /// - /// - public void SetVerticalOffset(double offset) - { - } - /// - /// Gets or sets a value that indicates whether scrolling on the vertical axis is possible. - /// - public bool CanVerticallyScroll - { - get { return false; } - set { } - } - /// - /// Gets or sets a value that indicates whether scrolling on the horizontal axis is possible. - /// - public bool CanHorizontallyScroll - { - get { return true; } - set { } - } - /// - /// Not implemented - /// - public double ExtentHeight - { - get { return 0.0; } - }/// - /// Not implemented - /// - - public double VerticalOffset - { - get { return 0.0; } - } - /// - /// Not implemented - /// - public double ViewportHeight - { - get { return 0.0; } - } - - // Gets scroll data info - private ScrollData ScrollData - { - get - { - return this.scrollData ?? (this.scrollData = new ScrollData()); - } - } - - // Scroll data info - private ScrollData scrollData; - - // Validates input offset - static double ValidateInputOffset(double offset, string parameterName) - { - if (double.IsNaN(offset)) - { - throw new ArgumentOutOfRangeException(parameterName); - } - - return Math.Max(0.0, offset); - } - - // Verifies scrolling data using the passed viewport and extent as newly computed values. - // Checks the X/Y offset and coerces them into the range [0, Extent - ViewportSize] - // If extent, viewport, or the newly coerced offsets are different than the existing offset, - // cachces are updated and InvalidateScrollInfo() is called. - private void VerifyScrollData(double viewportWidth, double extentWidth) - { - bool isValid = true; - - if (double.IsInfinity(viewportWidth)) - { - viewportWidth = extentWidth; - } - - double offsetX = CoerceOffset(this.ScrollData.OffsetX, extentWidth, viewportWidth); - - isValid &= DoubleUtil.AreClose(viewportWidth, this.ScrollData.ViewportWidth); - isValid &= DoubleUtil.AreClose(extentWidth, this.ScrollData.ExtentWidth); - isValid &= DoubleUtil.AreClose(this.ScrollData.OffsetX, offsetX); - - this.ScrollData.ViewportWidth = viewportWidth; - this.ScrollData.ExtentWidth = extentWidth; - this.ScrollData.OffsetX = offsetX; - - if (!isValid) - { - if (this.ScrollOwner != null) - { - this.ScrollOwner.InvalidateScrollInfo(); - } - } - } - - // Returns an offset coerced into the [0, Extent - Viewport] range. - static double CoerceOffset(double offset, double extent, double viewport) - { - if (offset > extent - viewport) - { - offset = extent - viewport; - } - - if (offset < 0) - { - offset = 0; - } - - return offset; - } - - #endregion - } -} +using System; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Media; + +namespace Fluent +{ + using Fluent.Internal; + + /// + /// Represent panel with ribbon group. + /// It is automatically adjusting size of controls + /// + public class RibbonGroupsContainer : Panel, IScrollInfo + { + #region Reduce Order + + /// + /// Gets or sets reduce order of group in the ribbon panel. + /// It must be enumerated with comma from the first to reduce to + /// the last to reduce (use Control.Name as group name in the enum). + /// Enclose in parentheses as (Control.Name) to reduce/enlarge + /// scalable elements in the given group + /// + public string ReduceOrder + { + get { return (string)this.GetValue(ReduceOrderProperty); } + set { this.SetValue(ReduceOrderProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for ReduceOrder. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty ReduceOrderProperty = + DependencyProperty.Register("ReduceOrder", typeof(string), typeof(RibbonGroupsContainer), new UIPropertyMetadata(ReduceOrderPropertyChanged)); + + // handles ReduseOrder property changed + static void ReduceOrderPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + RibbonGroupsContainer ribbonPanel = (RibbonGroupsContainer)d; + ribbonPanel.reduceOrder = ((string)e.NewValue).Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries); + ribbonPanel.reduceOrderIndex = ribbonPanel.reduceOrder.Length - 1; + + ribbonPanel.InvalidateMeasure(); + ribbonPanel.InvalidateArrange(); + } + + #endregion + + #region Fields + + private string[] reduceOrder = new string[0]; + private int reduceOrderIndex; + + #endregion + + #region Initialization + + /// + /// Default constructor + /// + public RibbonGroupsContainer() + : base() + { + this.Focusable = false; + } + + #endregion + + #region Layout Overridings + + /// + /// Returns a collection of the panel's UIElements. + /// + /// The logical parent of the collection to be created. + /// Returns an ordered collection of elements that have the specified logical parent. + protected override UIElementCollection CreateUIElementCollection(FrameworkElement logicalParent) + { + return new UIElementCollection(this, /*Parent as FrameworkElement*/this); + } + + /// + /// Measures all of the RibbonGroupBox, and resize them appropriately + /// to fit within the available room + /// + /// The available size that this element can give to child elements. + /// The size that the groups container determines it needs during + /// layout, based on its calculations of child element sizes. + /// + protected override Size MeasureOverride(Size availableSize) + { + var desiredSize = this.GetChildrenDesiredSizeIntermediate(); + + if (this.reduceOrder.Length == 0) + { + this.VerifyScrollData(availableSize.Width, desiredSize.Width); + return desiredSize; + } + + // If we have more available space - try to expand groups + while (desiredSize.Width <= availableSize.Width) + { + var hasMoreVariants = this.reduceOrderIndex < this.reduceOrder.Length - 1; + if (!hasMoreVariants) + { + break; + } + + // Increase size of another item + this.reduceOrderIndex++; + this.IncreaseGroupBoxSize(this.reduceOrder[this.reduceOrderIndex]); + + desiredSize = this.GetChildrenDesiredSizeIntermediate(); + } + + // If not enough space - go to next variant + while (desiredSize.Width > availableSize.Width) + { + var hasMoreVariants = this.reduceOrderIndex >= 0; + if (!hasMoreVariants) + { + break; + } + + // Decrease size of another item + this.DecreaseGroupBoxSize(this.reduceOrder[this.reduceOrderIndex]); + this.reduceOrderIndex--; + + desiredSize = this.GetChildrenDesiredSizeIntermediate(); + } + + // Set find values + foreach (var item in this.InternalChildren) + { + var groupBox = item as RibbonGroupBox; + if (groupBox == null) + { + continue; + } + + if ((groupBox.State != groupBox.StateIntermediate) || + (groupBox.Scale != groupBox.ScaleIntermediate)) + { + groupBox.SuppressCacheReseting = true; + groupBox.State = groupBox.StateIntermediate; + groupBox.Scale = groupBox.ScaleIntermediate; + groupBox.InvalidateLayout(); + groupBox.Measure(new Size(double.PositiveInfinity, availableSize.Height)); + groupBox.SuppressCacheReseting = false; + } + + // Something wrong with cache? + if (groupBox.DesiredSizeIntermediate != groupBox.DesiredSize) + { + // Reset cache and reinvoke masure + groupBox.ClearCache(); + return this.MeasureOverride(availableSize); + } + } + + this.VerifyScrollData(availableSize.Width, desiredSize.Width); + return desiredSize; + } + + private Size GetChildrenDesiredSizeIntermediate() + { + double width = 0; + double height = 0; + + foreach (UIElement child in this.InternalChildren) + { + var groupBox = child as RibbonGroupBox; + if (groupBox == null) + { + continue; + } + + var desiredSize = groupBox.DesiredSizeIntermediate; + width += desiredSize.Width; + height = Math.Max(height, desiredSize.Height); + } + + return new Size(width, height); + } + + // Increase size of the item + private void IncreaseGroupBoxSize(string name) + { + var groupBox = this.FindGroup(name); + var scale = name.StartsWith("(", StringComparison.OrdinalIgnoreCase); + if (groupBox == null) + { + return; + } + + if (scale) + { + groupBox.ScaleIntermediate++; + } + else + { + groupBox.StateIntermediate = groupBox.StateIntermediate != RibbonGroupBoxState.Large + ? groupBox.StateIntermediate - 1 + : RibbonGroupBoxState.Large; + } + } + + // Decrease size of the item + private void DecreaseGroupBoxSize(string name) + { + var groupBox = this.FindGroup(name); + var scale = name.StartsWith("(", StringComparison.OrdinalIgnoreCase); + if (groupBox == null) + { + return; + } + + if (scale) + { + groupBox.ScaleIntermediate--; + } + else + { + groupBox.StateIntermediate = groupBox.StateIntermediate != RibbonGroupBoxState.Collapsed + ? groupBox.StateIntermediate + 1 + : groupBox.StateIntermediate; + } + } + + private RibbonGroupBox FindGroup(string name) + { + if (name.StartsWith("(", StringComparison.OrdinalIgnoreCase)) + { + name = name.Substring(1, name.Length - 2); + } + + foreach (FrameworkElement child in this.InternalChildren) + { + if (child.Name == name) + { + return child as RibbonGroupBox; + } + } + return null; + } + + /// + /// When overridden in a derived class, positions child elements and determines + /// a size for a System.Windows.FrameworkElement derived class. + /// + /// The final area within the parent that this element should use to arrange itself and its children. + /// The actual size used. + protected override Size ArrangeOverride(Size finalSize) + { + var finalRect = new Rect(finalSize) + { + X = -this.HorizontalOffset + }; + foreach (UIElement item in this.InternalChildren) + { + finalRect.Width = item.DesiredSize.Width; + finalRect.Height = Math.Max(finalSize.Height, item.DesiredSize.Height); + item.Arrange(finalRect); + finalRect.X += item.DesiredSize.Width; + } + return finalSize; + } + + #endregion + + #region IScrollInfo Members + + /// + /// Gets or sets a System.Windows.Controls.ScrollViewer element that controls scrolling behavior. + /// + public ScrollViewer ScrollOwner + { + get { return this.ScrollData.ScrollOwner; } + set { this.ScrollData.ScrollOwner = value; } + } + + /// + /// Sets the amount of horizontal offset. + /// + /// The degree to which content is horizontally offset from the containing viewport. + public void SetHorizontalOffset(double offset) + { + var newValue = CoerceOffset(ValidateInputOffset(offset, "HorizontalOffset"), this.scrollData.ExtentWidth, this.scrollData.ViewportWidth); + if (DoubleUtil.AreClose(this.ScrollData.OffsetX, newValue) == false) + { + this.scrollData.OffsetX = newValue; + this.InvalidateArrange(); + } + } + /// + /// Gets the horizontal size of the extent. + /// + public double ExtentWidth + { + get { return this.ScrollData.ExtentWidth; } + } + + /// + /// Gets the horizontal offset of the scrolled content. + /// + public double HorizontalOffset + { + get { return this.ScrollData.OffsetX; } + } + + /// + /// Gets the horizontal size of the viewport for this content. + /// + public double ViewportWidth + { + get { return this.ScrollData.ViewportWidth; } + } + + /// + /// Scrolls left within content by one logical unit. + /// + public void LineLeft() + { + this.SetHorizontalOffset(this.HorizontalOffset - 16.0); + } + + /// + /// Scrolls right within content by one logical unit. + /// + public void LineRight() + { + this.SetHorizontalOffset(this.HorizontalOffset + 16.0); + } + + /// + /// Forces content to scroll until the coordinate space of a System.Windows.Media.Visual object is visible. + /// This is optimized for horizontal scrolling only + /// + /// A System.Windows.Media.Visual that becomes visible. + /// A bounding rectangle that identifies the coordinate space to make visible. + /// A System.Windows.Rect that is visible. + public Rect MakeVisible(Visual visual, Rect rectangle) + { + // We can only work on visuals that are us or children. + // An empty rect has no size or position. We can't meaningfully use it. + if (rectangle.IsEmpty + || visual == null + || ReferenceEquals(visual, this) + || !this.IsAncestorOf(visual)) + { + return Rect.Empty; + } + + // Compute the child's rect relative to (0,0) in our coordinate space. + GeneralTransform childTransform = visual.TransformToAncestor(this); + + rectangle = childTransform.TransformBounds(rectangle); + + // Initialize the viewport + Rect viewport = new Rect(this.HorizontalOffset, rectangle.Top, this.ViewportWidth, rectangle.Height); + rectangle.X += viewport.X; + + // Compute the offsets required to minimally scroll the child maximally into view. + double minX = ComputeScrollOffsetWithMinimalScroll(viewport.Left, viewport.Right, rectangle.Left, rectangle.Right); + + // We have computed the scrolling offsets; scroll to them. + this.SetHorizontalOffset(minX); + + // Compute the visible rectangle of the child relative to the viewport. + viewport.X = minX; + rectangle.Intersect(viewport); + + rectangle.X -= viewport.X; + + // Return the rectangle + return rectangle; + } + + static double ComputeScrollOffsetWithMinimalScroll( + double topView, + double bottomView, + double topChild, + double bottomChild) + { + // # CHILD POSITION CHILD SIZE SCROLL REMEDY + // 1 Above viewport <= viewport Down Align top edge of child & viewport + // 2 Above viewport > viewport Down Align bottom edge of child & viewport + // 3 Below viewport <= viewport Up Align bottom edge of child & viewport + // 4 Below viewport > viewport Up Align top edge of child & viewport + // 5 Entirely within viewport NA No scroll. + // 6 Spanning viewport NA No scroll. + // + // Note: "Above viewport" = childTop above viewportTop, childBottom above viewportBottom + // "Below viewport" = childTop below viewportTop, childBottom below viewportBottom + // These child thus may overlap with the viewport, but will scroll the same direction + /*bool fAbove = DoubleUtil.LessThan(topChild, topView) && DoubleUtil.LessThan(bottomChild, bottomView); + bool fBelow = DoubleUtil.GreaterThan(bottomChild, bottomView) && DoubleUtil.GreaterThan(topChild, topView);*/ + bool fAbove = (topChild < topView) && (bottomChild < bottomView); + bool fBelow = (bottomChild > bottomView) && (topChild > topView); + bool fLarger = (bottomChild - topChild) > (bottomView - topView); + + // Handle Cases: 1 & 4 above + if ((fAbove && !fLarger) + || (fBelow && fLarger)) + { + return topChild; + } + + // Handle Cases: 2 & 3 above + if (fAbove || fBelow) + { + return bottomChild - (bottomView - topView); + } + + // Handle cases: 5 & 6 above. + return topView; + } + + /// + /// Not implemented + /// + public void MouseWheelDown() + { + } + /// + /// Not implemented + /// + public void MouseWheelLeft() + { + } + /// + /// Not implemented + /// + public void MouseWheelRight() + { + } + /// + /// Not implemented + /// + public void MouseWheelUp() + { + } + /// + /// Not implemented + /// + public void LineDown() + { + } + /// + /// Not implemented + /// + public void LineUp() + { + } + /// + /// Not implemented + /// + public void PageDown() + { + } + /// + /// Not implemented + /// + public void PageLeft() + { + } + /// + /// Not implemented + /// + public void PageRight() + { + } + /// + /// Not implemented + /// + public void PageUp() + { + } + /// + /// Not implemented + /// + /// + public void SetVerticalOffset(double offset) + { + } + /// + /// Gets or sets a value that indicates whether scrolling on the vertical axis is possible. + /// + public bool CanVerticallyScroll + { + get { return false; } + set { } + } + /// + /// Gets or sets a value that indicates whether scrolling on the horizontal axis is possible. + /// + public bool CanHorizontallyScroll + { + get { return true; } + set { } + } + /// + /// Not implemented + /// + public double ExtentHeight + { + get { return 0.0; } + }/// + /// Not implemented + /// + + public double VerticalOffset + { + get { return 0.0; } + } + /// + /// Not implemented + /// + public double ViewportHeight + { + get { return 0.0; } + } + + // Gets scroll data info + private ScrollData ScrollData + { + get + { + return this.scrollData ?? (this.scrollData = new ScrollData()); + } + } + + // Scroll data info + private ScrollData scrollData; + + // Validates input offset + static double ValidateInputOffset(double offset, string parameterName) + { + if (double.IsNaN(offset)) + { + throw new ArgumentOutOfRangeException(parameterName); + } + + return Math.Max(0.0, offset); + } + + // Verifies scrolling data using the passed viewport and extent as newly computed values. + // Checks the X/Y offset and coerces them into the range [0, Extent - ViewportSize] + // If extent, viewport, or the newly coerced offsets are different than the existing offset, + // cachces are updated and InvalidateScrollInfo() is called. + private void VerifyScrollData(double viewportWidth, double extentWidth) + { + bool isValid = true; + + if (double.IsInfinity(viewportWidth)) + { + viewportWidth = extentWidth; + } + + double offsetX = CoerceOffset(this.ScrollData.OffsetX, extentWidth, viewportWidth); + + isValid &= DoubleUtil.AreClose(viewportWidth, this.ScrollData.ViewportWidth); + isValid &= DoubleUtil.AreClose(extentWidth, this.ScrollData.ExtentWidth); + isValid &= DoubleUtil.AreClose(this.ScrollData.OffsetX, offsetX); + + this.ScrollData.ViewportWidth = viewportWidth; + this.ScrollData.ExtentWidth = extentWidth; + this.ScrollData.OffsetX = offsetX; + + if (!isValid) + { + if (this.ScrollOwner != null) + { + this.ScrollOwner.InvalidateScrollInfo(); + } + } + } + + // Returns an offset coerced into the [0, Extent - Viewport] range. + static double CoerceOffset(double offset, double extent, double viewport) + { + if (offset > extent - viewport) + { + offset = extent - viewport; + } + + if (offset < 0) + { + offset = 0; + } + + return offset; + } + + #endregion + } +} diff --git a/Fluent/Controls/RibbonItemsControl.cs b/Fluent.Ribbon/Controls/RibbonItemsControl.cs similarity index 96% rename from Fluent/Controls/RibbonItemsControl.cs rename to Fluent.Ribbon/Controls/RibbonItemsControl.cs index 0a8150565..316ba26af 100644 --- a/Fluent/Controls/RibbonItemsControl.cs +++ b/Fluent.Ribbon/Controls/RibbonItemsControl.cs @@ -1,177 +1,177 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Markup; -using System.Windows.Media; - -namespace Fluent -{ - /// - /// Represents ribbon items control - /// - [ContentProperty("Items")] - public abstract class RibbonItemsControl : ItemsControl, IQuickAccessItemProvider, IRibbonControl - { - #region KeyTip - - /// - /// Gets or sets KeyTip for element. - /// - public string KeyTip - { - get { return (string)this.GetValue(KeyTipProperty); } - set { this.SetValue(KeyTipProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Keys. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty KeyTipProperty = Fluent.KeyTip.KeysProperty.AddOwner(typeof(RibbonItemsControl)); - - #endregion - - #region Size - - /// - /// Gets or sets Size for the element. - /// - public RibbonControlSize Size - { - get { return (RibbonControlSize)this.GetValue(SizeProperty); } - set { this.SetValue(SizeProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Size. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty SizeProperty = RibbonProperties.SizeProperty.AddOwner(typeof(RibbonItemsControl)); - - #endregion - - #region SizeDefinition - - /// - /// Gets or sets SizeDefinition for element. - /// - public RibbonControlSizeDefinition SizeDefinition - { - get { return (RibbonControlSizeDefinition)this.GetValue(SizeDefinitionProperty); } - set { this.SetValue(SizeDefinitionProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for SizeDefinition. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty SizeDefinitionProperty = RibbonProperties.SizeDefinitionProperty.AddOwner(typeof(RibbonItemsControl)); - - #endregion - - #region Header - - /// - /// Gets or sets element Text - /// - public object Header - { - get { return (string)this.GetValue(HeaderProperty); } - set { this.SetValue(HeaderProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Header. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty HeaderProperty = RibbonControl.HeaderProperty.AddOwner(typeof(RibbonItemsControl)); - - #endregion - - #region Icon - - /// - /// Gets or sets Icon for the element - /// - public object Icon - { - get { return (ImageSource)this.GetValue(IconProperty); } - set { this.SetValue(IconProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Icon. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IconProperty = RibbonControl.IconProperty.AddOwner(typeof(RibbonItemsControl)); - - #endregion - - #region Constructors - - /// - /// Static constructor - /// - [SuppressMessage("Microsoft.Performance", "CA1810")] - static RibbonItemsControl() - { - Type type = typeof(RibbonItemsControl); - ToolTipService.Attach(type); - ContextMenuService.Attach(type); - } - - /// - /// Default Constructor - /// - protected RibbonItemsControl() - { - ContextMenuService.Coerce(this); - } - - #endregion - - #region QuickAccess - - /// - /// Gets control which represents shortcut item. - /// This item MUST be syncronized with the original - /// and send command to original one control. - /// - /// Control which represents shortcut item - public abstract FrameworkElement CreateQuickAccessItem(); - - /// - /// Gets or sets whether control can be added to quick access toolbar - /// - public bool CanAddToQuickAccessToolBar - { - get { return (bool)this.GetValue(CanAddToQuickAccessToolBarProperty); } - set { this.SetValue(CanAddToQuickAccessToolBarProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for CanAddToQuickAccessToolBar. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty CanAddToQuickAccessToolBarProperty = RibbonControl.CanAddToQuickAccessToolBarProperty.AddOwner(typeof(RibbonItemsControl)); - - #endregion - - #region Methods - - /// - /// Handles key tip pressed - /// - public virtual void OnKeyTipPressed() - { - } - - /// - /// Handles back navigation with KeyTips - /// - public void OnKeyTipBack() - { - } - - #endregion - } +using System; +using System.Diagnostics.CodeAnalysis; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Markup; +using System.Windows.Media; + +namespace Fluent +{ + /// + /// Represents ribbon items control + /// + [ContentProperty("Items")] + public abstract class RibbonItemsControl : ItemsControl, IQuickAccessItemProvider, IRibbonControl + { + #region KeyTip + + /// + /// Gets or sets KeyTip for element. + /// + public string KeyTip + { + get { return (string)this.GetValue(KeyTipProperty); } + set { this.SetValue(KeyTipProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Keys. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty KeyTipProperty = Fluent.KeyTip.KeysProperty.AddOwner(typeof(RibbonItemsControl)); + + #endregion + + #region Size + + /// + /// Gets or sets Size for the element. + /// + public RibbonControlSize Size + { + get { return (RibbonControlSize)this.GetValue(SizeProperty); } + set { this.SetValue(SizeProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Size. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty SizeProperty = RibbonProperties.SizeProperty.AddOwner(typeof(RibbonItemsControl)); + + #endregion + + #region SizeDefinition + + /// + /// Gets or sets SizeDefinition for element. + /// + public RibbonControlSizeDefinition SizeDefinition + { + get { return (RibbonControlSizeDefinition)this.GetValue(SizeDefinitionProperty); } + set { this.SetValue(SizeDefinitionProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for SizeDefinition. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty SizeDefinitionProperty = RibbonProperties.SizeDefinitionProperty.AddOwner(typeof(RibbonItemsControl)); + + #endregion + + #region Header + + /// + /// Gets or sets element Text + /// + public object Header + { + get { return (string)this.GetValue(HeaderProperty); } + set { this.SetValue(HeaderProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Header. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty HeaderProperty = RibbonControl.HeaderProperty.AddOwner(typeof(RibbonItemsControl)); + + #endregion + + #region Icon + + /// + /// Gets or sets Icon for the element + /// + public object Icon + { + get { return (ImageSource)this.GetValue(IconProperty); } + set { this.SetValue(IconProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Icon. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IconProperty = RibbonControl.IconProperty.AddOwner(typeof(RibbonItemsControl)); + + #endregion + + #region Constructors + + /// + /// Static constructor + /// + [SuppressMessage("Microsoft.Performance", "CA1810")] + static RibbonItemsControl() + { + Type type = typeof(RibbonItemsControl); + ToolTipService.Attach(type); + ContextMenuService.Attach(type); + } + + /// + /// Default Constructor + /// + protected RibbonItemsControl() + { + ContextMenuService.Coerce(this); + } + + #endregion + + #region QuickAccess + + /// + /// Gets control which represents shortcut item. + /// This item MUST be syncronized with the original + /// and send command to original one control. + /// + /// Control which represents shortcut item + public abstract FrameworkElement CreateQuickAccessItem(); + + /// + /// Gets or sets whether control can be added to quick access toolbar + /// + public bool CanAddToQuickAccessToolBar + { + get { return (bool)this.GetValue(CanAddToQuickAccessToolBarProperty); } + set { this.SetValue(CanAddToQuickAccessToolBarProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for CanAddToQuickAccessToolBar. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty CanAddToQuickAccessToolBarProperty = RibbonControl.CanAddToQuickAccessToolBarProperty.AddOwner(typeof(RibbonItemsControl)); + + #endregion + + #region Methods + + /// + /// Handles key tip pressed + /// + public virtual void OnKeyTipPressed() + { + } + + /// + /// Handles back navigation with KeyTips + /// + public void OnKeyTipBack() + { + } + + #endregion + } } \ No newline at end of file diff --git a/Fluent/Controls/RibbonMenu.cs b/Fluent.Ribbon/Controls/RibbonMenu.cs similarity index 97% rename from Fluent/Controls/RibbonMenu.cs rename to Fluent.Ribbon/Controls/RibbonMenu.cs index 0e3433827..16055b062 100644 --- a/Fluent/Controls/RibbonMenu.cs +++ b/Fluent.Ribbon/Controls/RibbonMenu.cs @@ -1,97 +1,97 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Text; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Controls.Primitives; -using System.Windows.Input; -using System.Windows.Markup; -using System.Windows.Media; - -namespace Fluent -{ - /// - /// Represents menu in combo box and gallery - /// - [ContentProperty("Items")] - public class RibbonMenu : MenuBase - { - #region Constructors - - static RibbonMenu() - { - DefaultStyleKeyProperty.OverrideMetadata(typeof(RibbonMenu), new FrameworkPropertyMetadata(typeof(RibbonMenu))); - StyleProperty.OverrideMetadata(typeof(RibbonMenu), new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceStyle))); - } - - // Coerce object style - static object OnCoerceStyle(DependencyObject d, object basevalue) - { - if (basevalue == null) - { - basevalue = (d as FrameworkElement).TryFindResource(typeof(RibbonMenu)); - } - - return basevalue; - } - - #endregion - - #region Overrides - - /// - /// Creates or identifies the element that is used to display the given item. - /// - /// The element that is used to display the given item. - protected override DependencyObject GetContainerForItemOverride() - { - return new MenuItem(); - } - - /// - /// Determines if the specified item is (or is eligible to be) its own container. - /// - /// The item to check. - /// - protected override bool IsItemItsOwnContainerOverride(object item) - { - return item is System.Windows.Controls.MenuItem - || item is Separator; - } - - /// - /// Invoked when an unhandled  attached event reaches an element in its route that is derived from this class. Implement this method to add class handling for this event. - /// - /// The that contains the event data. - protected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e) - { - //base.OnGotKeyboardFocus(e); - IInputElement element = this.GetRootDropDownControl() as IInputElement; - if (element != null) Keyboard.Focus(element); - } - - /*protected override void OnGotMouseCapture(MouseEventArgs e) - { - IInputElement element = GetRootDropDownControl() as IInputElement; - if ((element!=null)&&(Mouse.Captured!=element)) Mouse.Capture(element, CaptureMode.SubTree); - }*/ - - private IDropDownControl GetRootDropDownControl() - { - DependencyObject element = this; - while (element != null) - { - IDropDownControl popup = element as IDropDownControl; - if (popup != null) return popup; - DependencyObject elementParent = VisualTreeHelper.GetParent(element); - if (elementParent == null) element = LogicalTreeHelper.GetParent(element); - else element = elementParent; - } - return null; - } - - #endregion - } -} +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Input; +using System.Windows.Markup; +using System.Windows.Media; + +namespace Fluent +{ + /// + /// Represents menu in combo box and gallery + /// + [ContentProperty("Items")] + public class RibbonMenu : MenuBase + { + #region Constructors + + static RibbonMenu() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(RibbonMenu), new FrameworkPropertyMetadata(typeof(RibbonMenu))); + StyleProperty.OverrideMetadata(typeof(RibbonMenu), new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceStyle))); + } + + // Coerce object style + static object OnCoerceStyle(DependencyObject d, object basevalue) + { + if (basevalue == null) + { + basevalue = (d as FrameworkElement).TryFindResource(typeof(RibbonMenu)); + } + + return basevalue; + } + + #endregion + + #region Overrides + + /// + /// Creates or identifies the element that is used to display the given item. + /// + /// The element that is used to display the given item. + protected override DependencyObject GetContainerForItemOverride() + { + return new MenuItem(); + } + + /// + /// Determines if the specified item is (or is eligible to be) its own container. + /// + /// The item to check. + /// + protected override bool IsItemItsOwnContainerOverride(object item) + { + return item is System.Windows.Controls.MenuItem + || item is Separator; + } + + /// + /// Invoked when an unhandled  attached event reaches an element in its route that is derived from this class. Implement this method to add class handling for this event. + /// + /// The that contains the event data. + protected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e) + { + //base.OnGotKeyboardFocus(e); + IInputElement element = this.GetRootDropDownControl() as IInputElement; + if (element != null) Keyboard.Focus(element); + } + + /*protected override void OnGotMouseCapture(MouseEventArgs e) + { + IInputElement element = GetRootDropDownControl() as IInputElement; + if ((element!=null)&&(Mouse.Captured!=element)) Mouse.Capture(element, CaptureMode.SubTree); + }*/ + + private IDropDownControl GetRootDropDownControl() + { + DependencyObject element = this; + while (element != null) + { + IDropDownControl popup = element as IDropDownControl; + if (popup != null) return popup; + DependencyObject elementParent = VisualTreeHelper.GetParent(element); + if (elementParent == null) element = LogicalTreeHelper.GetParent(element); + else element = elementParent; + } + return null; + } + + #endregion + } +} diff --git a/Fluent/Controls/RibbonScrollViewer.cs b/Fluent.Ribbon/Controls/RibbonScrollViewer.cs similarity index 97% rename from Fluent/Controls/RibbonScrollViewer.cs rename to Fluent.Ribbon/Controls/RibbonScrollViewer.cs index 895c6fd89..466d5aa53 100644 --- a/Fluent/Controls/RibbonScrollViewer.cs +++ b/Fluent.Ribbon/Controls/RibbonScrollViewer.cs @@ -1,27 +1,27 @@ -using System.Windows.Controls; -using System.Windows.Media; - -namespace Fluent -{ - /// - /// Represents ScrollViewer with modified hit test - /// - public class RibbonScrollViewer : ScrollViewer - { - /// - /// Performs a hit test to determine whether the specified - /// points are within the bounds of this ScrollViewer - /// - /// The result of the hit test - /// The parameters for hit testing within a visual object - protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters) - { - if (this.VisualChildrenCount > 0) - { - return VisualTreeHelper.HitTest(this.GetVisualChild(0), hitTestParameters.HitPoint); - } - - return base.HitTestCore(hitTestParameters); - } - } +using System.Windows.Controls; +using System.Windows.Media; + +namespace Fluent +{ + /// + /// Represents ScrollViewer with modified hit test + /// + public class RibbonScrollViewer : ScrollViewer + { + /// + /// Performs a hit test to determine whether the specified + /// points are within the bounds of this ScrollViewer + /// + /// The result of the hit test + /// The parameters for hit testing within a visual object + protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters) + { + if (this.VisualChildrenCount > 0) + { + return VisualTreeHelper.HitTest(this.GetVisualChild(0), hitTestParameters.HitPoint); + } + + return base.HitTestCore(hitTestParameters); + } + } } \ No newline at end of file diff --git a/Fluent/Controls/RibbonTabControl.cs b/Fluent.Ribbon/Controls/RibbonTabControl.cs similarity index 97% rename from Fluent/Controls/RibbonTabControl.cs rename to Fluent.Ribbon/Controls/RibbonTabControl.cs index a6e48a3d3..61f5c0e59 100644 --- a/Fluent/Controls/RibbonTabControl.cs +++ b/Fluent.Ribbon/Controls/RibbonTabControl.cs @@ -1,821 +1,821 @@ -namespace Fluent -{ - using System; - using System.Collections.Generic; - using System.Collections.ObjectModel; - using System.Collections.Specialized; - using System.ComponentModel; - using System.Diagnostics.CodeAnalysis; - using System.Linq; - using System.Runtime.InteropServices; - using System.Windows; - using System.Windows.Controls; - using System.Windows.Controls.Primitives; - using System.Windows.Input; - using System.Windows.Media; - using Fluent.Metro.Native; - - /// - /// Represents ribbon tab control - /// - [StyleTypedProperty(Property = "ItemContainerStyle", StyleTargetType = typeof(RibbonTabItem))] - [TemplatePart(Name = "PART_Popup", Type = typeof(Popup))] - [TemplatePart(Name = "PART_TabsContainer", Type = typeof(IScrollInfo))] - [TemplatePart(Name = "PART_ToolbarPanel", Type = typeof(Panel))] - public class RibbonTabControl : Selector, IDropDownControl - { - #region Fields - - // Collection of toolbar items - private ObservableCollection toolBarItems; - - // ToolBar panel - private Panel toolbarPanel; - - #endregion - - #region Events - - /// - /// Event which is fired when the, maybe listening, should be closed - /// - public event EventHandler RequestBackstageClose; - - #endregion - - #region Properties - - #region Menu - - /// - /// Gets or sets file menu control (can be application menu button, backstage button and so on) - /// - public UIElement Menu - { - get { return (UIElement)this.GetValue(MenuProperty); } - set { this.SetValue(MenuProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Button. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty MenuProperty = - DependencyProperty.Register("Menu", typeof(UIElement), - typeof(RibbonTabControl), new UIPropertyMetadata(null)); - - #endregion - - /// - /// Gets drop down popup - /// - public Popup DropDownPopup { get; private set; } - - /// - /// Gets a value indicating whether context menu is opened - /// - public bool IsContextMenuOpened { get; set; } - - /// - /// Gets content of selected tab item - /// - [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] - public object SelectedContent - { - get - { - return this.GetValue(SelectedContentProperty); - } - internal set - { - this.SetValue(SelectedContentPropertyKey, value); - } - } - - // DependencyProperty key for SelectedContent - static readonly DependencyPropertyKey SelectedContentPropertyKey = DependencyProperty.RegisterReadOnly("SelectedContent", typeof(object), typeof(RibbonTabControl), new FrameworkPropertyMetadata(null)); - - /// - /// Using a DependencyProperty as the backing store for . This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty SelectedContentProperty = SelectedContentPropertyKey.DependencyProperty; - - /// - /// Gets or sets whether ribbon is minimized - /// - public bool IsMinimized - { - get { return (bool)this.GetValue(IsMinimizedProperty); } - set { this.SetValue(IsMinimizedProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for . This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IsMinimizedProperty = DependencyProperty.Register("IsMinimized", typeof(bool), typeof(RibbonTabControl), new UIPropertyMetadata(false, OnMinimizedChanged)); - - /// - /// Gets or sets whether ribbon popup is opened - /// - public bool IsDropDownOpen - { - get { return (bool)this.GetValue(IsDropDownOpenProperty); } - set { this.SetValue(IsDropDownOpenProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for . This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IsDropDownOpenProperty = DependencyProperty.Register("IsDropDownOpen", typeof(bool), typeof(RibbonTabControl), new UIPropertyMetadata(false, OnIsDropDownOpenChanged, CoerceIsDropDownOpen)); - - private static object CoerceIsDropDownOpen(DependencyObject d, object basevalue) - { - var tabControl = d as RibbonTabControl; - - if (tabControl == null) - { - return basevalue; - } - - if (!tabControl.IsMinimized) - { - return false; - } - - return basevalue; - } - - /// - /// Defines if the currently selected item should draw it's highlight/selected borders - /// - public bool HighlightSelectedItem - { - get { return (bool)this.GetValue(HighlightSelectedItemProperty); } - set { this.SetValue(HighlightSelectedItemProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for . This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty HighlightSelectedItemProperty = - DependencyProperty.RegisterAttached("HighlightSelectedItem", typeof(bool), typeof(RibbonTabControl), new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.Inherits)); - - /// - /// Gets whether ribbon tabs can scroll - /// - internal bool CanScroll - { - get - { - var scrollInfo = this.GetTemplateChild("PART_TabsContainer") as IScrollInfo; - if (scrollInfo != null) - { - return scrollInfo.ExtentWidth > scrollInfo.ViewportWidth; - } - - return false; - } - } - - /// - /// Gets or sets selected tab item - /// - internal RibbonTabItem SelectedTabItem - { - get { return (RibbonTabItem)this.GetValue(SelectedTabItemProperty); } - private set { this.SetValue(SelectedTabItemProperty, value); } - } - - // Using a DependencyProperty as the backing store for SelectedTabItem. This enables animation, styling, binding, etc... - internal static readonly DependencyProperty SelectedTabItemProperty = - DependencyProperty.Register("SelectedTabItem", typeof(RibbonTabItem), typeof(RibbonTabControl), new UIPropertyMetadata(null)); - - /// - /// Gets collection of ribbon toolbar items - /// - public ObservableCollection ToolBarItems - { - get - { - if (this.toolBarItems == null) - { - this.toolBarItems = new ObservableCollection(); - this.toolBarItems.CollectionChanged += this.OnToolbarItemsCollectionChanged; - } - - return this.toolBarItems; - } - } - - internal Panel ToolbarPanel - { - get { return this.toolbarPanel; } - } - - // Handle toolbar iitems changes - private void OnToolbarItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) - { - if (this.ToolbarPanel == null) - { - return; - } - - switch (e.Action) - { - case NotifyCollectionChangedAction.Add: - for (var i = 0; i < e.NewItems.Count; i++) - { - this.ToolbarPanel.Children.Insert(e.NewStartingIndex + i, (UIElement)e.NewItems[i]); - } - break; - - case NotifyCollectionChangedAction.Remove: - foreach (var obj3 in e.OldItems.OfType()) - { - this.ToolbarPanel.Children.Remove(obj3); - } - break; - - case NotifyCollectionChangedAction.Replace: - foreach (var obj4 in e.OldItems.OfType()) - { - this.ToolbarPanel.Children.Remove(obj4); - } - foreach (var obj5 in e.NewItems.OfType()) - { - this.ToolbarPanel.Children.Add(obj5); - } - break; - - case NotifyCollectionChangedAction.Reset: - this.toolbarPanel.Children.Clear(); - foreach (var toolBarItem in this.ToolBarItems) - { - this.ToolbarPanel.Children.Add(toolBarItem); - } - break; - } - - } - - /// - /// Gets or sets the height of the gap between the ribbon and the content - /// - public double ContentGapHeight - { - get { return (double)this.GetValue(ContentGapHeightProperty); } - set { this.SetValue(ContentGapHeightProperty, value); } - } - - /// - /// DependencyProperty for - /// - public static readonly DependencyProperty ContentGapHeightProperty = - DependencyProperty.Register("ContentGapHeight", typeof(double), typeof(RibbonTabControl), new UIPropertyMetadata(5D)); - - #endregion - - #region Initializion - - /// - /// Static constructor - /// - [SuppressMessage("Microsoft.Performance", "CA1810")] - static RibbonTabControl() - { - var type = typeof(RibbonTabControl); - DefaultStyleKeyProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(typeof(RibbonTabControl))); - ContextMenuService.Attach(type); - PopupService.Attach(type); - StyleProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(null, OnCoerceStyle)); - } - - // Coerce object style - private static object OnCoerceStyle(DependencyObject d, object basevalue) - { - if (basevalue == null) - { - basevalue = ((FrameworkElement)d).TryFindResource(typeof(RibbonTabControl)); - } - - return basevalue; - } - - /// - /// Default constructor - /// - public RibbonTabControl() - { - ContextMenuService.Coerce(this); - - this.Loaded += this.OnLoaded; - this.Unloaded += this.OnUnloaded; - } - - #endregion - - #region Overrides - - /// - /// Raises the System.Windows.FrameworkElement.Initialized event. - /// This method is invoked whenever System.Windows. - /// FrameworkElement.IsInitialized is set to true internally. - /// - /// The System.Windows.RoutedEventArgs that contains the event data. - protected override void OnInitialized(EventArgs e) - { - base.OnInitialized(e); - this.ItemContainerGenerator.StatusChanged += this.OnGeneratorStatusChanged; - } - - /// - /// Creates or identifies the element that is used to display the given item. - /// - /// The element that is used to display the given item. - protected override DependencyObject GetContainerForItemOverride() - { - return new RibbonTabItem(); - } - - /// - /// Determines if the specified item is (or is eligible to be) its own container. - /// - /// The item to check. - /// true if the item is (or is eligible to be) its own container; otherwise, false. - protected override bool IsItemItsOwnContainerOverride(object item) - { - return item is RibbonTabItem; - } - - /// - /// When overridden in a derived class, is invoked whenever application code or - /// internal processes call System.Windows.FrameworkElement.ApplyTemplate(). - /// - public override void OnApplyTemplate() - { - this.DropDownPopup = this.Template.FindName("PART_Popup", this) as Popup; - if (this.DropDownPopup != null) - { - this.DropDownPopup.CustomPopupPlacementCallback = this.CustomPopupPlacementMethod; - } - - if (this.ToolbarPanel != null - && this.toolBarItems != null) - { - for (var i = 0; i < this.toolBarItems.Count; i++) - { - this.ToolbarPanel.Children.Remove(this.toolBarItems[i]); - } - } - - this.toolbarPanel = this.Template.FindName("PART_ToolbarPanel", this) as Panel; - - if (this.ToolbarPanel != null - && this.toolBarItems != null) - { - for (var i = 0; i < this.toolBarItems.Count; i++) - { - this.ToolbarPanel.Children.Add(this.toolBarItems[i]); - } - } - } - - /// - /// Updates the current selection when an item in the System.Windows.Controls.Primitives.Selector has changed - /// - /// The event data. - protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e) - { - base.OnItemsChanged(e); - - if (this.IsMinimized - && this.IsDropDownOpen == false) - { - return; - } - - if (e.Action == NotifyCollectionChangedAction.Remove - && this.SelectedIndex == -1) - { - var startIndex = e.OldStartingIndex + 1; - if (startIndex > this.Items.Count) - { - startIndex = 0; - } - - var item = this.FindNextTabItem(startIndex, -1); - if (item != null) - { - item.IsSelected = true; - } - else - { - this.SelectedContent = null; - } - } - } - - /// - /// Called when the selection changes. - /// - /// The event data. - protected override void OnSelectionChanged(SelectionChangedEventArgs e) - { - this.UpdateSelectedContent(); - - if (e.AddedItems.Count > 0) - { - if (this.IsMinimized) - { - this.IsDropDownOpen = true; - - ((RibbonTabItem)e.AddedItems[0]).IsHitTestVisible = false; - } - } - else - { - if (this.IsDropDownOpen) - { - this.IsDropDownOpen = false; - } - } - - if (e.RemovedItems.Count > 0) - { - ((RibbonTabItem)e.RemovedItems[0]).IsHitTestVisible = true; - } - - base.OnSelectionChanged(e); - } - - /// - /// Invoked when an unhandled System.Windows.Input.Mouse.PreviewMouseWheel  - /// attached event reaches an element in its route that is derived from this class. - /// Implement this method to add class handling for this event. - /// - /// The System.Windows.Input.MouseWheelEventArgs that contains the event data. - protected override void OnPreviewMouseWheel(MouseWheelEventArgs e) - { - //base.OnPreviewMouseWheel(e); - this.ProcessMouseWheel(e); - } - - /// - /// Invoked when the event is received. - /// - /// Information about the event. - protected override void OnKeyDown(KeyEventArgs e) - { - this.OnKeyUp(e); - - if (e.Handled) - { - return; - } - - switch (e.Key) - { - case Key.Escape: - if (this.IsDropDownOpen) - { - this.IsDropDownOpen = false; - } - break; - } - } - - #endregion - - #region Private methods - - private static bool IsRibbonAncestorOf(DependencyObject element) - { - while (element != null) - { - if (element is Ribbon) - { - return true; - } - - var parent = LogicalTreeHelper.GetParent(element) ?? VisualTreeHelper.GetParent(element); - - element = parent; - } - - return false; - } - - // Process mouse wheel event - internal void ProcessMouseWheel(MouseWheelEventArgs e) - { - if (this.IsMinimized - || this.SelectedItem == null) - { - return; - } - - var focusedElement = Keyboard.FocusedElement as DependencyObject; - - if (focusedElement != null - && IsRibbonAncestorOf(focusedElement)) - { - return; - } - - var visualItems = new List(); - var selectedIndex = -1; - -#if NET45 - var tabs = this.ItemContainerGenerator.Items.OfType() - .Where(x => x.Visibility == Visibility.Visible && (x.IsContextual == false || (x.IsContextual && x.Group.Visibility == Visibility.Visible))) - .OrderBy(x => x.IsContextual) - .ToList(); -#else - var tabs = this.Items.OfType().Select(x => this.ItemContainerGenerator.ContainerFromItem(x)).OfType() - .Where(x => x.Visibility == Visibility.Visible && (x.IsContextual == false || (x.IsContextual && x.Group.Visibility == Visibility.Visible))) - .OrderBy(x => x.IsContextual) - .ToList(); -#endif - - for (var i = 0; i < tabs.Count; i++) - { - var ribbonTabItem = tabs[i]; - visualItems.Add(ribbonTabItem); - - if (ribbonTabItem.IsSelected) - { - selectedIndex = visualItems.Count - 1; - } - } - - if (e.Delta > 0) - { - if (selectedIndex > 0) - { - visualItems[selectedIndex].IsSelected = false; - selectedIndex--; - visualItems[selectedIndex].IsSelected = true; - } - } - else if (e.Delta < 0) - { - if (selectedIndex < visualItems.Count - 1) - { - visualItems[selectedIndex].IsSelected = false; - selectedIndex++; - visualItems[selectedIndex].IsSelected = true; - } - } - - e.Handled = true; - } - - // Get selected ribbon tab item - private RibbonTabItem GetSelectedTabItem() - { - var selectedItem = this.SelectedItem; - if (selectedItem == null) - { - return null; - } - - var item = selectedItem as RibbonTabItem - ?? this.ItemContainerGenerator.ContainerFromIndex(this.SelectedIndex) as RibbonTabItem; - - return item; - } - - // Find next tab item - private RibbonTabItem FindNextTabItem(int startIndex, int direction) - { - if (direction != 0) - { - var index = startIndex; - for (var i = 0; i < this.Items.Count; i++) - { - index += direction; - - if (index >= this.Items.Count) - { - index = 0; - } - else if (index < 0) - { - index = this.Items.Count - 1; - } - - var nextItem = this.ItemContainerGenerator.ContainerFromIndex(index) as RibbonTabItem; - if (((nextItem != null) && nextItem.IsEnabled) && (nextItem.Visibility == Visibility.Visible)) - { - return nextItem; - } - } - } - - return null; - } - - // Updates selected content - private void UpdateSelectedContent() - { - if (this.SelectedIndex < 0) - { - this.SelectedContent = null; - this.SelectedTabItem = null; - } - else - { - var selectedTabItem = this.GetSelectedTabItem(); - if (selectedTabItem != null) - { - this.SelectedContent = selectedTabItem.GroupsContainer; - this.SelectedTabItem = selectedTabItem; - } - } - } - - #endregion - - #region Event handling - - private void OnLoaded(object sender, RoutedEventArgs e) - { - } - - private void OnUnloaded(object sender, RoutedEventArgs e) - { - } - - // Handles GeneratorStatus changed - private void OnGeneratorStatusChanged(object sender, EventArgs e) - { - if (this.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated) - { - this.UpdateSelectedContent(); - } - } - - // Handles IsMinimized changed - private static void OnMinimizedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var tab = (RibbonTabControl)d; - - if (!tab.IsMinimized) - { - tab.IsDropDownOpen = false; - } - - if ((bool)e.NewValue == false - && tab.SelectedIndex < 0) - { - var item = tab.FindNextTabItem(-1, 1); - - if (item != null) - { - item.IsSelected = true; - } - } - } - - // Handles ribbon popup closing - private void OnRibbonTabPopupClosing() - { - var ribbonTabItem = this.SelectedItem as RibbonTabItem; - - if (ribbonTabItem != null) - { - ribbonTabItem.IsHitTestVisible = true; - } - - if (Mouse.Captured == this) - { - Mouse.Capture(null); - } - } - - // handles ribbon popup opening - private void OnRibbonTabPopupOpening() - { - var ribbonTabItem = this.SelectedItem as RibbonTabItem; - - if (ribbonTabItem != null) - { - ribbonTabItem.IsHitTestVisible = false; - } - - Mouse.Capture(this, CaptureMode.SubTree); - } - - /// - /// Implements custom placement for ribbon popup - /// - /// - /// - /// - /// - private CustomPopupPlacement[] CustomPopupPlacementMethod(Size popupsize, Size targetsize, Point offset) - { - if (this.DropDownPopup == null - || this.SelectedTabItem == null) - { - return null; - } - - // Get current workarea - var tabItemPos = this.SelectedTabItem.PointToScreen(new Point(0, 0)); - var tabItemRect = new RECT - { - left = (int)tabItemPos.X, - top = (int)tabItemPos.Y, - right = (int)tabItemPos.X + (int)this.SelectedTabItem.ActualWidth, - bottom = (int)tabItemPos.Y + (int)this.SelectedTabItem.ActualHeight - }; - - const uint MONITOR_DEFAULTTONEAREST = 0x00000002; - - var monitor = NativeMethods.MonitorFromRect(ref tabItemRect, MONITOR_DEFAULTTONEAREST); - if (monitor == IntPtr.Zero) - { - return null; - } - - var monitorInfo = new MONITORINFO(); - monitorInfo.cbSize = Marshal.SizeOf(monitorInfo); - UnsafeNativeMethods.GetMonitorInfo(monitor, monitorInfo); - - var startPoint = this.PointToScreen(new Point(0, 0)); - if (this.FlowDirection == FlowDirection.RightToLeft) - { - startPoint.X -= this.ActualWidth; - } - - var inWindowRibbonWidth = monitorInfo.rcWork.right - Math.Max(monitorInfo.rcWork.left, startPoint.X); - - var actualWidth = this.ActualWidth; - if (startPoint.X < monitorInfo.rcWork.left) - { - actualWidth -= monitorInfo.rcWork.left - startPoint.X; - startPoint.X = monitorInfo.rcWork.left; - } - - // Set width and prevent negative values - this.DropDownPopup.Width = Math.Max(0, Math.Min(actualWidth, inWindowRibbonWidth)); - return new[] - { - new CustomPopupPlacement(new Point(startPoint.X - tabItemPos.X + offset.X, targetsize.Height + offset.Y), PopupPrimaryAxis.Vertical), - new CustomPopupPlacement(new Point(startPoint.X - tabItemPos.X + offset.X, -1 * (targetsize.Height + offset.Y + ((ScrollViewer)this.SelectedContent).ActualHeight)), PopupPrimaryAxis.Vertical) - }; - } - - // Handles IsDropDownOpen property changed - private static void OnIsDropDownOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var ribbonTabControl = (RibbonTabControl)d; - - ribbonTabControl.RaiseRequestBackstageClose(); - - if (ribbonTabControl.IsDropDownOpen) - { - ribbonTabControl.OnRibbonTabPopupOpening(); - } - else - { - ribbonTabControl.OnRibbonTabPopupClosing(); - } - } - - /// - /// Raises an event causing the Backstage-View to be closed - /// - public void RaiseRequestBackstageClose() - { - var handler = this.RequestBackstageClose; - - if (handler != null) - { - handler(this, null); - } - } - - #endregion - - /// - /// Gets the first visible item - /// - public object GetFirstVisibleItem() - { - foreach (var item in this.Items) - { - var ribbonTab = this.ItemContainerGenerator.ContainerFromItem(item) as RibbonTabItem; - - if (ribbonTab != null - && ribbonTab.Visibility == Visibility.Visible) - { - return ribbonTab; - } - } - - return null; - } - } +namespace Fluent +{ + using System; + using System.Collections.Generic; + using System.Collections.ObjectModel; + using System.Collections.Specialized; + using System.ComponentModel; + using System.Diagnostics.CodeAnalysis; + using System.Linq; + using System.Runtime.InteropServices; + using System.Windows; + using System.Windows.Controls; + using System.Windows.Controls.Primitives; + using System.Windows.Input; + using System.Windows.Media; + using Fluent.Metro.Native; + + /// + /// Represents ribbon tab control + /// + [StyleTypedProperty(Property = "ItemContainerStyle", StyleTargetType = typeof(RibbonTabItem))] + [TemplatePart(Name = "PART_Popup", Type = typeof(Popup))] + [TemplatePart(Name = "PART_TabsContainer", Type = typeof(IScrollInfo))] + [TemplatePart(Name = "PART_ToolbarPanel", Type = typeof(Panel))] + public class RibbonTabControl : Selector, IDropDownControl + { + #region Fields + + // Collection of toolbar items + private ObservableCollection toolBarItems; + + // ToolBar panel + private Panel toolbarPanel; + + #endregion + + #region Events + + /// + /// Event which is fired when the, maybe listening, should be closed + /// + public event EventHandler RequestBackstageClose; + + #endregion + + #region Properties + + #region Menu + + /// + /// Gets or sets file menu control (can be application menu button, backstage button and so on) + /// + public UIElement Menu + { + get { return (UIElement)this.GetValue(MenuProperty); } + set { this.SetValue(MenuProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Button. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty MenuProperty = + DependencyProperty.Register("Menu", typeof(UIElement), + typeof(RibbonTabControl), new UIPropertyMetadata(null)); + + #endregion + + /// + /// Gets drop down popup + /// + public Popup DropDownPopup { get; private set; } + + /// + /// Gets a value indicating whether context menu is opened + /// + public bool IsContextMenuOpened { get; set; } + + /// + /// Gets content of selected tab item + /// + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public object SelectedContent + { + get + { + return this.GetValue(SelectedContentProperty); + } + internal set + { + this.SetValue(SelectedContentPropertyKey, value); + } + } + + // DependencyProperty key for SelectedContent + static readonly DependencyPropertyKey SelectedContentPropertyKey = DependencyProperty.RegisterReadOnly("SelectedContent", typeof(object), typeof(RibbonTabControl), new FrameworkPropertyMetadata(null)); + + /// + /// Using a DependencyProperty as the backing store for . This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty SelectedContentProperty = SelectedContentPropertyKey.DependencyProperty; + + /// + /// Gets or sets whether ribbon is minimized + /// + public bool IsMinimized + { + get { return (bool)this.GetValue(IsMinimizedProperty); } + set { this.SetValue(IsMinimizedProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for . This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IsMinimizedProperty = DependencyProperty.Register("IsMinimized", typeof(bool), typeof(RibbonTabControl), new UIPropertyMetadata(false, OnMinimizedChanged)); + + /// + /// Gets or sets whether ribbon popup is opened + /// + public bool IsDropDownOpen + { + get { return (bool)this.GetValue(IsDropDownOpenProperty); } + set { this.SetValue(IsDropDownOpenProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for . This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IsDropDownOpenProperty = DependencyProperty.Register("IsDropDownOpen", typeof(bool), typeof(RibbonTabControl), new UIPropertyMetadata(false, OnIsDropDownOpenChanged, CoerceIsDropDownOpen)); + + private static object CoerceIsDropDownOpen(DependencyObject d, object basevalue) + { + var tabControl = d as RibbonTabControl; + + if (tabControl == null) + { + return basevalue; + } + + if (!tabControl.IsMinimized) + { + return false; + } + + return basevalue; + } + + /// + /// Defines if the currently selected item should draw it's highlight/selected borders + /// + public bool HighlightSelectedItem + { + get { return (bool)this.GetValue(HighlightSelectedItemProperty); } + set { this.SetValue(HighlightSelectedItemProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for . This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty HighlightSelectedItemProperty = + DependencyProperty.RegisterAttached("HighlightSelectedItem", typeof(bool), typeof(RibbonTabControl), new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.Inherits)); + + /// + /// Gets whether ribbon tabs can scroll + /// + internal bool CanScroll + { + get + { + var scrollInfo = this.GetTemplateChild("PART_TabsContainer") as IScrollInfo; + if (scrollInfo != null) + { + return scrollInfo.ExtentWidth > scrollInfo.ViewportWidth; + } + + return false; + } + } + + /// + /// Gets or sets selected tab item + /// + internal RibbonTabItem SelectedTabItem + { + get { return (RibbonTabItem)this.GetValue(SelectedTabItemProperty); } + private set { this.SetValue(SelectedTabItemProperty, value); } + } + + // Using a DependencyProperty as the backing store for SelectedTabItem. This enables animation, styling, binding, etc... + internal static readonly DependencyProperty SelectedTabItemProperty = + DependencyProperty.Register("SelectedTabItem", typeof(RibbonTabItem), typeof(RibbonTabControl), new UIPropertyMetadata(null)); + + /// + /// Gets collection of ribbon toolbar items + /// + public ObservableCollection ToolBarItems + { + get + { + if (this.toolBarItems == null) + { + this.toolBarItems = new ObservableCollection(); + this.toolBarItems.CollectionChanged += this.OnToolbarItemsCollectionChanged; + } + + return this.toolBarItems; + } + } + + internal Panel ToolbarPanel + { + get { return this.toolbarPanel; } + } + + // Handle toolbar iitems changes + private void OnToolbarItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + if (this.ToolbarPanel == null) + { + return; + } + + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + for (var i = 0; i < e.NewItems.Count; i++) + { + this.ToolbarPanel.Children.Insert(e.NewStartingIndex + i, (UIElement)e.NewItems[i]); + } + break; + + case NotifyCollectionChangedAction.Remove: + foreach (var obj3 in e.OldItems.OfType()) + { + this.ToolbarPanel.Children.Remove(obj3); + } + break; + + case NotifyCollectionChangedAction.Replace: + foreach (var obj4 in e.OldItems.OfType()) + { + this.ToolbarPanel.Children.Remove(obj4); + } + foreach (var obj5 in e.NewItems.OfType()) + { + this.ToolbarPanel.Children.Add(obj5); + } + break; + + case NotifyCollectionChangedAction.Reset: + this.toolbarPanel.Children.Clear(); + foreach (var toolBarItem in this.ToolBarItems) + { + this.ToolbarPanel.Children.Add(toolBarItem); + } + break; + } + + } + + /// + /// Gets or sets the height of the gap between the ribbon and the content + /// + public double ContentGapHeight + { + get { return (double)this.GetValue(ContentGapHeightProperty); } + set { this.SetValue(ContentGapHeightProperty, value); } + } + + /// + /// DependencyProperty for + /// + public static readonly DependencyProperty ContentGapHeightProperty = + DependencyProperty.Register("ContentGapHeight", typeof(double), typeof(RibbonTabControl), new UIPropertyMetadata(5D)); + + #endregion + + #region Initializion + + /// + /// Static constructor + /// + [SuppressMessage("Microsoft.Performance", "CA1810")] + static RibbonTabControl() + { + var type = typeof(RibbonTabControl); + DefaultStyleKeyProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(typeof(RibbonTabControl))); + ContextMenuService.Attach(type); + PopupService.Attach(type); + StyleProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(null, OnCoerceStyle)); + } + + // Coerce object style + private static object OnCoerceStyle(DependencyObject d, object basevalue) + { + if (basevalue == null) + { + basevalue = ((FrameworkElement)d).TryFindResource(typeof(RibbonTabControl)); + } + + return basevalue; + } + + /// + /// Default constructor + /// + public RibbonTabControl() + { + ContextMenuService.Coerce(this); + + this.Loaded += this.OnLoaded; + this.Unloaded += this.OnUnloaded; + } + + #endregion + + #region Overrides + + /// + /// Raises the System.Windows.FrameworkElement.Initialized event. + /// This method is invoked whenever System.Windows. + /// FrameworkElement.IsInitialized is set to true internally. + /// + /// The System.Windows.RoutedEventArgs that contains the event data. + protected override void OnInitialized(EventArgs e) + { + base.OnInitialized(e); + this.ItemContainerGenerator.StatusChanged += this.OnGeneratorStatusChanged; + } + + /// + /// Creates or identifies the element that is used to display the given item. + /// + /// The element that is used to display the given item. + protected override DependencyObject GetContainerForItemOverride() + { + return new RibbonTabItem(); + } + + /// + /// Determines if the specified item is (or is eligible to be) its own container. + /// + /// The item to check. + /// true if the item is (or is eligible to be) its own container; otherwise, false. + protected override bool IsItemItsOwnContainerOverride(object item) + { + return item is RibbonTabItem; + } + + /// + /// When overridden in a derived class, is invoked whenever application code or + /// internal processes call System.Windows.FrameworkElement.ApplyTemplate(). + /// + public override void OnApplyTemplate() + { + this.DropDownPopup = this.Template.FindName("PART_Popup", this) as Popup; + if (this.DropDownPopup != null) + { + this.DropDownPopup.CustomPopupPlacementCallback = this.CustomPopupPlacementMethod; + } + + if (this.ToolbarPanel != null + && this.toolBarItems != null) + { + for (var i = 0; i < this.toolBarItems.Count; i++) + { + this.ToolbarPanel.Children.Remove(this.toolBarItems[i]); + } + } + + this.toolbarPanel = this.Template.FindName("PART_ToolbarPanel", this) as Panel; + + if (this.ToolbarPanel != null + && this.toolBarItems != null) + { + for (var i = 0; i < this.toolBarItems.Count; i++) + { + this.ToolbarPanel.Children.Add(this.toolBarItems[i]); + } + } + } + + /// + /// Updates the current selection when an item in the System.Windows.Controls.Primitives.Selector has changed + /// + /// The event data. + protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e) + { + base.OnItemsChanged(e); + + if (this.IsMinimized + && this.IsDropDownOpen == false) + { + return; + } + + if (e.Action == NotifyCollectionChangedAction.Remove + && this.SelectedIndex == -1) + { + var startIndex = e.OldStartingIndex + 1; + if (startIndex > this.Items.Count) + { + startIndex = 0; + } + + var item = this.FindNextTabItem(startIndex, -1); + if (item != null) + { + item.IsSelected = true; + } + else + { + this.SelectedContent = null; + } + } + } + + /// + /// Called when the selection changes. + /// + /// The event data. + protected override void OnSelectionChanged(SelectionChangedEventArgs e) + { + this.UpdateSelectedContent(); + + if (e.AddedItems.Count > 0) + { + if (this.IsMinimized) + { + this.IsDropDownOpen = true; + + ((RibbonTabItem)e.AddedItems[0]).IsHitTestVisible = false; + } + } + else + { + if (this.IsDropDownOpen) + { + this.IsDropDownOpen = false; + } + } + + if (e.RemovedItems.Count > 0) + { + ((RibbonTabItem)e.RemovedItems[0]).IsHitTestVisible = true; + } + + base.OnSelectionChanged(e); + } + + /// + /// Invoked when an unhandled System.Windows.Input.Mouse.PreviewMouseWheel  + /// attached event reaches an element in its route that is derived from this class. + /// Implement this method to add class handling for this event. + /// + /// The System.Windows.Input.MouseWheelEventArgs that contains the event data. + protected override void OnPreviewMouseWheel(MouseWheelEventArgs e) + { + //base.OnPreviewMouseWheel(e); + this.ProcessMouseWheel(e); + } + + /// + /// Invoked when the event is received. + /// + /// Information about the event. + protected override void OnKeyDown(KeyEventArgs e) + { + this.OnKeyUp(e); + + if (e.Handled) + { + return; + } + + switch (e.Key) + { + case Key.Escape: + if (this.IsDropDownOpen) + { + this.IsDropDownOpen = false; + } + break; + } + } + + #endregion + + #region Private methods + + private static bool IsRibbonAncestorOf(DependencyObject element) + { + while (element != null) + { + if (element is Ribbon) + { + return true; + } + + var parent = LogicalTreeHelper.GetParent(element) ?? VisualTreeHelper.GetParent(element); + + element = parent; + } + + return false; + } + + // Process mouse wheel event + internal void ProcessMouseWheel(MouseWheelEventArgs e) + { + if (this.IsMinimized + || this.SelectedItem == null) + { + return; + } + + var focusedElement = Keyboard.FocusedElement as DependencyObject; + + if (focusedElement != null + && IsRibbonAncestorOf(focusedElement)) + { + return; + } + + var visualItems = new List(); + var selectedIndex = -1; + +#if NET45 + var tabs = this.ItemContainerGenerator.Items.OfType() + .Where(x => x.Visibility == Visibility.Visible && (x.IsContextual == false || (x.IsContextual && x.Group.Visibility == Visibility.Visible))) + .OrderBy(x => x.IsContextual) + .ToList(); +#else + var tabs = this.Items.OfType().Select(x => this.ItemContainerGenerator.ContainerFromItem(x)).OfType() + .Where(x => x.Visibility == Visibility.Visible && (x.IsContextual == false || (x.IsContextual && x.Group.Visibility == Visibility.Visible))) + .OrderBy(x => x.IsContextual) + .ToList(); +#endif + + for (var i = 0; i < tabs.Count; i++) + { + var ribbonTabItem = tabs[i]; + visualItems.Add(ribbonTabItem); + + if (ribbonTabItem.IsSelected) + { + selectedIndex = visualItems.Count - 1; + } + } + + if (e.Delta > 0) + { + if (selectedIndex > 0) + { + visualItems[selectedIndex].IsSelected = false; + selectedIndex--; + visualItems[selectedIndex].IsSelected = true; + } + } + else if (e.Delta < 0) + { + if (selectedIndex < visualItems.Count - 1) + { + visualItems[selectedIndex].IsSelected = false; + selectedIndex++; + visualItems[selectedIndex].IsSelected = true; + } + } + + e.Handled = true; + } + + // Get selected ribbon tab item + private RibbonTabItem GetSelectedTabItem() + { + var selectedItem = this.SelectedItem; + if (selectedItem == null) + { + return null; + } + + var item = selectedItem as RibbonTabItem + ?? this.ItemContainerGenerator.ContainerFromIndex(this.SelectedIndex) as RibbonTabItem; + + return item; + } + + // Find next tab item + private RibbonTabItem FindNextTabItem(int startIndex, int direction) + { + if (direction != 0) + { + var index = startIndex; + for (var i = 0; i < this.Items.Count; i++) + { + index += direction; + + if (index >= this.Items.Count) + { + index = 0; + } + else if (index < 0) + { + index = this.Items.Count - 1; + } + + var nextItem = this.ItemContainerGenerator.ContainerFromIndex(index) as RibbonTabItem; + if (((nextItem != null) && nextItem.IsEnabled) && (nextItem.Visibility == Visibility.Visible)) + { + return nextItem; + } + } + } + + return null; + } + + // Updates selected content + private void UpdateSelectedContent() + { + if (this.SelectedIndex < 0) + { + this.SelectedContent = null; + this.SelectedTabItem = null; + } + else + { + var selectedTabItem = this.GetSelectedTabItem(); + if (selectedTabItem != null) + { + this.SelectedContent = selectedTabItem.GroupsContainer; + this.SelectedTabItem = selectedTabItem; + } + } + } + + #endregion + + #region Event handling + + private void OnLoaded(object sender, RoutedEventArgs e) + { + } + + private void OnUnloaded(object sender, RoutedEventArgs e) + { + } + + // Handles GeneratorStatus changed + private void OnGeneratorStatusChanged(object sender, EventArgs e) + { + if (this.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated) + { + this.UpdateSelectedContent(); + } + } + + // Handles IsMinimized changed + private static void OnMinimizedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var tab = (RibbonTabControl)d; + + if (!tab.IsMinimized) + { + tab.IsDropDownOpen = false; + } + + if ((bool)e.NewValue == false + && tab.SelectedIndex < 0) + { + var item = tab.FindNextTabItem(-1, 1); + + if (item != null) + { + item.IsSelected = true; + } + } + } + + // Handles ribbon popup closing + private void OnRibbonTabPopupClosing() + { + var ribbonTabItem = this.SelectedItem as RibbonTabItem; + + if (ribbonTabItem != null) + { + ribbonTabItem.IsHitTestVisible = true; + } + + if (Mouse.Captured == this) + { + Mouse.Capture(null); + } + } + + // handles ribbon popup opening + private void OnRibbonTabPopupOpening() + { + var ribbonTabItem = this.SelectedItem as RibbonTabItem; + + if (ribbonTabItem != null) + { + ribbonTabItem.IsHitTestVisible = false; + } + + Mouse.Capture(this, CaptureMode.SubTree); + } + + /// + /// Implements custom placement for ribbon popup + /// + /// + /// + /// + /// + private CustomPopupPlacement[] CustomPopupPlacementMethod(Size popupsize, Size targetsize, Point offset) + { + if (this.DropDownPopup == null + || this.SelectedTabItem == null) + { + return null; + } + + // Get current workarea + var tabItemPos = this.SelectedTabItem.PointToScreen(new Point(0, 0)); + var tabItemRect = new RECT + { + left = (int)tabItemPos.X, + top = (int)tabItemPos.Y, + right = (int)tabItemPos.X + (int)this.SelectedTabItem.ActualWidth, + bottom = (int)tabItemPos.Y + (int)this.SelectedTabItem.ActualHeight + }; + + const uint MONITOR_DEFAULTTONEAREST = 0x00000002; + + var monitor = NativeMethods.MonitorFromRect(ref tabItemRect, MONITOR_DEFAULTTONEAREST); + if (monitor == IntPtr.Zero) + { + return null; + } + + var monitorInfo = new MONITORINFO(); + monitorInfo.cbSize = Marshal.SizeOf(monitorInfo); + UnsafeNativeMethods.GetMonitorInfo(monitor, monitorInfo); + + var startPoint = this.PointToScreen(new Point(0, 0)); + if (this.FlowDirection == FlowDirection.RightToLeft) + { + startPoint.X -= this.ActualWidth; + } + + var inWindowRibbonWidth = monitorInfo.rcWork.right - Math.Max(monitorInfo.rcWork.left, startPoint.X); + + var actualWidth = this.ActualWidth; + if (startPoint.X < monitorInfo.rcWork.left) + { + actualWidth -= monitorInfo.rcWork.left - startPoint.X; + startPoint.X = monitorInfo.rcWork.left; + } + + // Set width and prevent negative values + this.DropDownPopup.Width = Math.Max(0, Math.Min(actualWidth, inWindowRibbonWidth)); + return new[] + { + new CustomPopupPlacement(new Point(startPoint.X - tabItemPos.X + offset.X, targetsize.Height + offset.Y), PopupPrimaryAxis.Vertical), + new CustomPopupPlacement(new Point(startPoint.X - tabItemPos.X + offset.X, -1 * (targetsize.Height + offset.Y + ((ScrollViewer)this.SelectedContent).ActualHeight)), PopupPrimaryAxis.Vertical) + }; + } + + // Handles IsDropDownOpen property changed + private static void OnIsDropDownOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var ribbonTabControl = (RibbonTabControl)d; + + ribbonTabControl.RaiseRequestBackstageClose(); + + if (ribbonTabControl.IsDropDownOpen) + { + ribbonTabControl.OnRibbonTabPopupOpening(); + } + else + { + ribbonTabControl.OnRibbonTabPopupClosing(); + } + } + + /// + /// Raises an event causing the Backstage-View to be closed + /// + public void RaiseRequestBackstageClose() + { + var handler = this.RequestBackstageClose; + + if (handler != null) + { + handler(this, null); + } + } + + #endregion + + /// + /// Gets the first visible item + /// + public object GetFirstVisibleItem() + { + foreach (var item in this.Items) + { + var ribbonTab = this.ItemContainerGenerator.ContainerFromItem(item) as RibbonTabItem; + + if (ribbonTab != null + && ribbonTab.Visibility == Visibility.Visible) + { + return ribbonTab; + } + } + + return null; + } + } } \ No newline at end of file diff --git a/Fluent/Controls/RibbonTabItem.cs b/Fluent.Ribbon/Controls/RibbonTabItem.cs similarity index 97% rename from Fluent/Controls/RibbonTabItem.cs rename to Fluent.Ribbon/Controls/RibbonTabItem.cs index 36a54ac03..8f412d50f 100644 --- a/Fluent/Controls/RibbonTabItem.cs +++ b/Fluent.Ribbon/Controls/RibbonTabItem.cs @@ -1,745 +1,745 @@ -using System; -using System.Collections; -using System.Collections.ObjectModel; -using System.Collections.Specialized; -using System.ComponentModel; -using System.Diagnostics.CodeAnalysis; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Controls.Primitives; -using System.Windows.Input; -using System.Windows.Markup; -using System.Windows.Media; - -namespace Fluent -{ - using System.Linq; - using System.Windows.Data; - - /// - /// Represents ribbon tab item - /// - [TemplatePart(Name = "PART_ContentContainer", Type = typeof(Border))] - [ContentProperty("Groups")] - [DefaultProperty("Groups")] - [DefaultEvent("IsSelectedChanged")] - public class RibbonTabItem : Control, IKeyTipedControl, IHeaderedControl - { - #region Fields - - // Content container - private Border contentContainer; - - // Desired width - private double desiredWidth; - - // Collection of ribbon groups - private ObservableCollection groups; - - // Ribbon groups container - private readonly RibbonGroupsContainer groupsInnerContainer = new RibbonGroupsContainer(); - private readonly ScrollViewer groupsContainer = new ScrollViewer(); - - // Cached width - private double cachedWidth; - - #endregion - - #region Properties - - #region KeyTip - - /// - /// Gets or sets KeyTip for element. - /// - public string KeyTip - { - get { return (string)this.GetValue(KeyTipProperty); } - set { this.SetValue(KeyTipProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Keys. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty KeyTipProperty = Fluent.KeyTip.KeysProperty.AddOwner(typeof(RibbonTabItem)); - - #endregion - - /// - /// Gets ribbon groups container - /// - public ScrollViewer GroupsContainer - { - get { return this.groupsContainer; } - } - - /// - /// Gets or sets whether ribbon is minimized - /// - public bool IsMinimized - { - get { return (bool)this.GetValue(IsMinimizedProperty); } - set { this.SetValue(IsMinimizedProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for IsMinimized. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IsMinimizedProperty = DependencyProperty.Register("IsMinimized", typeof(bool), typeof(RibbonTabItem), new UIPropertyMetadata(false)); - - /// - /// Gets or sets whether ribbon is opened - /// - public bool IsOpen - { - get { return (bool)this.GetValue(IsOpenProperty); } - set { this.SetValue(IsOpenProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for IsOpen. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IsOpenProperty = DependencyProperty.Register("IsOpen", typeof(bool), typeof(RibbonTabItem), new UIPropertyMetadata(false)); - - /// - /// Gets or sets reduce order - /// - public string ReduceOrder - { - get { return this.groupsInnerContainer.ReduceOrder; } - set { this.groupsInnerContainer.ReduceOrder = value; } - } - - #region IsContextual - - /// - /// Gets or sets whether tab item is contextual - /// - public bool IsContextual - { - get { return (bool)this.GetValue(IsContextualProperty); } - private set { this.SetValue(IsContextualPropertyKey, value); } - } - - private static readonly DependencyPropertyKey IsContextualPropertyKey = - DependencyProperty.RegisterReadOnly("IsContextual", typeof(bool), typeof(RibbonTabItem), new UIPropertyMetadata(false)); - - /// - /// Using a DependencyProperty as the backing store for IsContextual. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IsContextualProperty = IsContextualPropertyKey.DependencyProperty; - - /// - /// Gets an enumerator for logical child elements of this element. - /// - protected override IEnumerator LogicalChildren - { - get - { - yield return this.groupsContainer; - } - } - - #endregion - - /// - /// Gets or sets whether tab item is selected - /// - [Bindable(true), Category("Appearance")] - public bool IsSelected - { - get - { - return (bool)this.GetValue(IsSelectedProperty); - } - set - { - this.SetValue(IsSelectedProperty, value); - } - } - - /// - /// Using a DependencyProperty as the backing store for IsSelected. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IsSelectedProperty = Selector.IsSelectedProperty.AddOwner(typeof(RibbonTabItem), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.Journal | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.AffectsParentMeasure, new PropertyChangedCallback(OnIsSelectedChanged))); - - /// - /// Gets ribbon tab control parent - /// - internal RibbonTabControl TabControlParent - { - get - { - return (ItemsControl.ItemsControlFromItemContainer(this) as RibbonTabControl); - } - } - - - /// - /// Gets or sets indent - /// - public double Indent - { - get { return (double)this.GetValue(IndentProperty); } - set { this.SetValue(IndentProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for HeaderMargin. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IndentProperty = - DependencyProperty.Register("Indent", typeof(double), typeof(RibbonTabItem), new UIPropertyMetadata((double)12.0)); - - /// - /// Gets or sets whether separator is visible - /// - public bool IsSeparatorVisible - { - get { return (bool)this.GetValue(IsSeparatorVisibleProperty); } - set { this.SetValue(IsSeparatorVisibleProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for IsSeparatorVisible. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IsSeparatorVisibleProperty = - DependencyProperty.Register("IsSeparatorVisible", typeof(bool), typeof(RibbonTabItem), new UIPropertyMetadata(false)); - - /// - /// Gets or sets ribbon contextual tab group - /// - public RibbonContextualTabGroup Group - { - get { return (RibbonContextualTabGroup)this.GetValue(GroupProperty); } - set { this.SetValue(GroupProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Group. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty GroupProperty = - DependencyProperty.Register("Group", typeof(RibbonContextualTabGroup), typeof(RibbonTabItem), new UIPropertyMetadata(null, OnGroupChanged)); - - // handles Group property chanhged - private static void OnGroupChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var tab = (RibbonTabItem)d; - - if (e.OldValue != null) - { - ((RibbonContextualTabGroup)e.OldValue).RemoveTabItem(tab); - } - - if (e.NewValue != null) - { - var tabGroup = (RibbonContextualTabGroup)e.NewValue; - tabGroup.AppendTabItem(tab); - tab.IsContextual = true; - } - else - { - tab.IsContextual = false; - } - } - - /// - /// Gets or sets desired width of the tab item - /// - internal double DesiredWidth - { - get { return this.desiredWidth; } - set - { - this.desiredWidth = value; - this.InvalidateMeasure(); - } - } - - /// - /// Gets or sets whether tab item has left group border - /// - public bool HasLeftGroupBorder - { - get { return (bool)this.GetValue(HasLeftGroupBorderProperty); } - set { this.SetValue(HasLeftGroupBorderProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for HaseLeftGroupBorder. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty HasLeftGroupBorderProperty = - DependencyProperty.Register("HasLeftGroupBorder", typeof(bool), typeof(RibbonTabItem), new UIPropertyMetadata(false)); - - /// - /// Gets or sets whether tab item has right group border - /// - public bool HasRightGroupBorder - { - get { return (bool)this.GetValue(HasRightGroupBorderProperty); } - set { this.SetValue(HasRightGroupBorderProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for HaseLeftGroupBorder. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty HasRightGroupBorderProperty = - DependencyProperty.Register("HasRightGroupBorder", typeof(bool), typeof(RibbonTabItem), new UIPropertyMetadata(false)); - - /// - /// get collection of ribbon groups - /// - public ObservableCollection Groups - { - get - { - if (this.groups == null) - { - this.groups = new ObservableCollection(); - this.groups.CollectionChanged += this.OnGroupsCollectionChanged; - } - return this.groups; - } - } - - // handles ribbon groups collection changes - private void OnGroupsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) - { - if (this.groupsInnerContainer == null) - { - return; - } - - switch (e.Action) - { - case NotifyCollectionChangedAction.Add: - for (int i = 0; i < e.NewItems.Count; i++) - { - this.groupsInnerContainer.Children.Insert(e.NewStartingIndex + i, (UIElement)e.NewItems[i]); - } - break; - - case NotifyCollectionChangedAction.Remove: - foreach (var item in e.OldItems.OfType()) - { - this.groupsInnerContainer.Children.Remove(item); - } - break; - - case NotifyCollectionChangedAction.Replace: - foreach (var item in e.OldItems.OfType()) - { - this.groupsInnerContainer.Children.Remove(item); - } - foreach (var item in e.NewItems.OfType()) - { - this.groupsInnerContainer.Children.Add(item); - } - break; - - case NotifyCollectionChangedAction.Reset: - this.groupsInnerContainer.Children.Clear(); - - foreach (var group in this.groups) - { - this.groupsInnerContainer.Children.Add(group); - } - break; - } - - } - - #region Header Property - - /// - /// Gets or sets header of tab item - /// - public object Header - { - get { return (object)this.GetValue(HeaderProperty); } - set { this.SetValue(HeaderProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Header. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty HeaderProperty = - DependencyProperty.Register("Header", typeof(object), typeof(RibbonTabItem), new UIPropertyMetadata(null, OnHeaderChanged)); - - // Header changed handler - static void OnHeaderChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - RibbonTabItem tabItem = (RibbonTabItem)d; - tabItem.CoerceValue(ToolTipProperty); - } - - #endregion - - #region Focusable - - /// - /// Handles IsEnabled changes - /// - /// - /// The event data. - private static void OnFocusableChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - } - - /// - /// Coerces IsEnabled - /// - /// - /// - /// - private static object CoerceFocusable(DependencyObject d, object basevalue) - { - var control = d as RibbonTabItem; - if (control != null) - { - var ribbon = control.FindParentRibbon(); - if (ribbon != null) - { - return ((bool)basevalue) - && ribbon.Focusable; - } - } - - return basevalue; - } - - // Find parent ribbon - private Ribbon FindParentRibbon() - { - var element = this.Parent; - while (element != null) - { - var ribbon = element as Ribbon; - if (ribbon != null) - { - return ribbon; - } - - element = VisualTreeHelper.GetParent(element); - } - - return null; - } - - #endregion - - #endregion - - #region Initialize - - /// - /// Static constructor - /// - [SuppressMessage("Microsoft.Performance", "CA1810")] - static RibbonTabItem() - { - DefaultStyleKeyProperty.OverrideMetadata(typeof(RibbonTabItem), new FrameworkPropertyMetadata(typeof(RibbonTabItem))); - FocusableProperty.AddOwner(typeof(RibbonTabItem), new FrameworkPropertyMetadata(OnFocusableChanged, CoerceFocusable)); - ToolTipProperty.OverrideMetadata(typeof(RibbonTabItem), new FrameworkPropertyMetadata(null, CoerceToolTip)); - VisibilityProperty.AddOwner(typeof(RibbonTabItem), new FrameworkPropertyMetadata(OnVisibilityChanged)); - StyleProperty.OverrideMetadata(typeof(RibbonTabItem), new FrameworkPropertyMetadata(null, OnCoerceStyle)); - } - - // Coerce object style - static object OnCoerceStyle(DependencyObject d, object basevalue) - { - if (basevalue == null) - { - basevalue = (d as FrameworkElement).TryFindResource(typeof(RibbonTabItem)); - } - - return basevalue; - } - - // Handles visibility changes - private static void OnVisibilityChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var item = d as RibbonTabItem; - - if (item == null) - { - return; - } - - if (item.Group != null) - { - item.Group.UpdateInnerVisiblityAndGroupBorders(); - } - - if (item.IsSelected - && (Visibility)e.NewValue == Visibility.Collapsed) - { - if (item.TabControlParent != null) - { - if (item.TabControlParent.IsMinimized) - { - item.IsSelected = false; - } - else - { - item.TabControlParent.SelectedItem = item.TabControlParent.GetFirstVisibleItem(); - } - } - } - } - - // Coerce ToolTip to ensure that tooltip displays name of the tabitem - private static object CoerceToolTip(DependencyObject d, object basevalue) - { - var tabItem = (RibbonTabItem)d; - if (basevalue == null - && tabItem.Header is string) - { - basevalue = tabItem.Header; - } - - return basevalue; - } - - /// - /// Default constructor - /// - public RibbonTabItem() - { - this.AddLogicalChild(this.groupsContainer); - this.groupsContainer.Content = this.groupsInnerContainer; - - // Force redirection of DataContext. This is needed, because we detach the container from the visual tree and attach it to a diffrent one (the popup/dropdown) when the ribbon is minimized. - this.groupsInnerContainer.SetBinding(DataContextProperty, new Binding("DataContext") - { - Source = this - }); - - ContextMenuService.Coerce(this); - - this.Loaded += this.OnLoaded; - this.Unloaded += this.OnUnloaded; - } - - #endregion - - #region Overrides - - /// - /// Called to remeasure a control. - /// - /// The maximum size that the method can return. - /// The size of the control, up to the maximum specified by constraint. - protected override Size MeasureOverride(Size constraint) - { - if (this.contentContainer == null) - { - return base.MeasureOverride(constraint); - } - - if (this.IsContextual && this.Group != null && this.Group.Visibility == Visibility.Collapsed) - { - return Size.Empty; - } - - this.contentContainer.Padding = new Thickness(this.Indent, this.contentContainer.Padding.Top, this.Indent, this.contentContainer.Padding.Bottom); - Size baseConstraint = base.MeasureOverride(constraint); - double totalWidth = this.contentContainer.DesiredSize.Width - this.contentContainer.Margin.Left - this.contentContainer.Margin.Right; - (this.contentContainer.Child).Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); - double headerWidth = this.contentContainer.Child.DesiredSize.Width; - if (totalWidth < headerWidth + this.Indent * 2) - { - double newPaddings = Math.Max(0, (totalWidth - headerWidth) / 2); - this.contentContainer.Padding = new Thickness(newPaddings, this.contentContainer.Padding.Top, newPaddings, this.contentContainer.Padding.Bottom); - } - else - { - if (this.desiredWidth != 0) - { - // If header width is larger then tab increase tab width - if ((constraint.Width > this.desiredWidth) && (this.desiredWidth > totalWidth)) baseConstraint.Width = this.desiredWidth; - else - baseConstraint.Width = headerWidth + this.Indent * 2 + this.contentContainer.Margin.Left + this.contentContainer.Margin.Right; - } - } - - if ((this.cachedWidth != baseConstraint.Width) && (this.IsContextual) && (this.Group != null)) - { - this.cachedWidth = baseConstraint.Width; - FrameworkElement parent = (VisualTreeHelper.GetParent(this.Group) as FrameworkElement); - if (parent != null) parent.InvalidateMeasure(); - } - - return baseConstraint; - } - - /// - /// On new style applying - /// - public override void OnApplyTemplate() - { - this.contentContainer = this.GetTemplateChild("PART_ContentContainer") as Border; - } - - /// - /// Invoked when an unhandled System.Windows.UIElement.MouseLeftButtonDownrouted event is raised - /// on this element. Implement this method to add class handling for this event. - /// - /// The System.Windows.Input.MouseButtonEventArgs that contains the event data. - /// The event data reports that the left mouse button was pressed. - protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) - { - if (e.Source == this - && e.ClickCount == 2) - { - e.Handled = true; - - if (this.TabControlParent != null) - { - this.TabControlParent.IsMinimized = !this.TabControlParent.IsMinimized; - } - } - else if (e.Source == this - || !this.IsSelected) - { - if (this.Visibility == Visibility.Visible) - { - if (this.TabControlParent != null) - { - var newItem = this.TabControlParent.ItemContainerGenerator.ItemFromContainer(this); - - if (this.TabControlParent.SelectedTabItem == newItem) - { - this.TabControlParent.IsDropDownOpen = !this.TabControlParent.IsDropDownOpen; - } - else - { - this.TabControlParent.SelectedItem = newItem; - } - - this.TabControlParent.RaiseRequestBackstageClose(); - } - else - { - this.IsSelected = true; - } - - e.Handled = true; - } - } - } - - #endregion - - #region Private methods - - // Handles IsSelected property changes - private static void OnIsSelectedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - RibbonTabItem container = d as RibbonTabItem; - bool newValue = (bool)e.NewValue; - if (newValue) - { - if ((container.TabControlParent != null) && (container.TabControlParent.SelectedItem is RibbonTabItem) && (container.TabControlParent.SelectedItem != container)) - (container.TabControlParent.SelectedItem as RibbonTabItem).IsSelected = false; - container.OnSelected(new RoutedEventArgs(Selector.SelectedEvent, container)); - } - else - { - container.OnUnselected(new RoutedEventArgs(Selector.UnselectedEvent, container)); - } - - } - /// - /// Handles selected - /// - /// The event data - protected virtual void OnSelected(RoutedEventArgs e) - { - this.HandleIsSelectedChanged(e); - } - /// - /// handles unselected - /// - /// The event data - protected virtual void OnUnselected(RoutedEventArgs e) - { - this.HandleIsSelectedChanged(e); - } - - #endregion - - #region Event handling - - // Handles IsSelected property changes - private void HandleIsSelectedChanged(RoutedEventArgs e) - { - this.RaiseEvent(e); - } - - private void OnLoaded(object sender, RoutedEventArgs e) - { - this.SubscribeEvents(); - } - - private void OnUnloaded(object sender, RoutedEventArgs e) - { - this.UnSubscribeEvents(); - } - - private void SubscribeEvents() - { - // Always unsubscribe events to ensure we don't subscribe twice - this.UnSubscribeEvents(); - - if (this.groups != null) - { - this.groups.CollectionChanged += this.OnGroupsCollectionChanged; - } - } - - private void UnSubscribeEvents() - { - if (this.groups != null) - { - this.groups.CollectionChanged -= this.OnGroupsCollectionChanged; - } - } - - #endregion - - /// - /// Handles key tip pressed - /// - public void OnKeyTipPressed() - { - if (this.TabControlParent != null) - { - var currentSelectedItem = this.TabControlParent.SelectedItem as RibbonTabItem; - - if (currentSelectedItem != null) - { - currentSelectedItem.IsSelected = false; - } - } - - this.IsSelected = true; - } - - /// - /// Handles back navigation with KeyTips - /// - public void OnKeyTipBack() - { - if (this.TabControlParent != null - && this.TabControlParent.IsMinimized) - { - this.TabControlParent.IsDropDownOpen = false; - } - } - } +using System; +using System.Collections; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Input; +using System.Windows.Markup; +using System.Windows.Media; + +namespace Fluent +{ + using System.Linq; + using System.Windows.Data; + + /// + /// Represents ribbon tab item + /// + [TemplatePart(Name = "PART_ContentContainer", Type = typeof(Border))] + [ContentProperty("Groups")] + [DefaultProperty("Groups")] + [DefaultEvent("IsSelectedChanged")] + public class RibbonTabItem : Control, IKeyTipedControl, IHeaderedControl + { + #region Fields + + // Content container + private Border contentContainer; + + // Desired width + private double desiredWidth; + + // Collection of ribbon groups + private ObservableCollection groups; + + // Ribbon groups container + private readonly RibbonGroupsContainer groupsInnerContainer = new RibbonGroupsContainer(); + private readonly ScrollViewer groupsContainer = new ScrollViewer(); + + // Cached width + private double cachedWidth; + + #endregion + + #region Properties + + #region KeyTip + + /// + /// Gets or sets KeyTip for element. + /// + public string KeyTip + { + get { return (string)this.GetValue(KeyTipProperty); } + set { this.SetValue(KeyTipProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Keys. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty KeyTipProperty = Fluent.KeyTip.KeysProperty.AddOwner(typeof(RibbonTabItem)); + + #endregion + + /// + /// Gets ribbon groups container + /// + public ScrollViewer GroupsContainer + { + get { return this.groupsContainer; } + } + + /// + /// Gets or sets whether ribbon is minimized + /// + public bool IsMinimized + { + get { return (bool)this.GetValue(IsMinimizedProperty); } + set { this.SetValue(IsMinimizedProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for IsMinimized. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IsMinimizedProperty = DependencyProperty.Register("IsMinimized", typeof(bool), typeof(RibbonTabItem), new UIPropertyMetadata(false)); + + /// + /// Gets or sets whether ribbon is opened + /// + public bool IsOpen + { + get { return (bool)this.GetValue(IsOpenProperty); } + set { this.SetValue(IsOpenProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for IsOpen. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IsOpenProperty = DependencyProperty.Register("IsOpen", typeof(bool), typeof(RibbonTabItem), new UIPropertyMetadata(false)); + + /// + /// Gets or sets reduce order + /// + public string ReduceOrder + { + get { return this.groupsInnerContainer.ReduceOrder; } + set { this.groupsInnerContainer.ReduceOrder = value; } + } + + #region IsContextual + + /// + /// Gets or sets whether tab item is contextual + /// + public bool IsContextual + { + get { return (bool)this.GetValue(IsContextualProperty); } + private set { this.SetValue(IsContextualPropertyKey, value); } + } + + private static readonly DependencyPropertyKey IsContextualPropertyKey = + DependencyProperty.RegisterReadOnly("IsContextual", typeof(bool), typeof(RibbonTabItem), new UIPropertyMetadata(false)); + + /// + /// Using a DependencyProperty as the backing store for IsContextual. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IsContextualProperty = IsContextualPropertyKey.DependencyProperty; + + /// + /// Gets an enumerator for logical child elements of this element. + /// + protected override IEnumerator LogicalChildren + { + get + { + yield return this.groupsContainer; + } + } + + #endregion + + /// + /// Gets or sets whether tab item is selected + /// + [Bindable(true), Category("Appearance")] + public bool IsSelected + { + get + { + return (bool)this.GetValue(IsSelectedProperty); + } + set + { + this.SetValue(IsSelectedProperty, value); + } + } + + /// + /// Using a DependencyProperty as the backing store for IsSelected. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IsSelectedProperty = Selector.IsSelectedProperty.AddOwner(typeof(RibbonTabItem), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.Journal | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.AffectsParentMeasure, new PropertyChangedCallback(OnIsSelectedChanged))); + + /// + /// Gets ribbon tab control parent + /// + internal RibbonTabControl TabControlParent + { + get + { + return (ItemsControl.ItemsControlFromItemContainer(this) as RibbonTabControl); + } + } + + + /// + /// Gets or sets indent + /// + public double Indent + { + get { return (double)this.GetValue(IndentProperty); } + set { this.SetValue(IndentProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for HeaderMargin. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IndentProperty = + DependencyProperty.Register("Indent", typeof(double), typeof(RibbonTabItem), new UIPropertyMetadata((double)12.0)); + + /// + /// Gets or sets whether separator is visible + /// + public bool IsSeparatorVisible + { + get { return (bool)this.GetValue(IsSeparatorVisibleProperty); } + set { this.SetValue(IsSeparatorVisibleProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for IsSeparatorVisible. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IsSeparatorVisibleProperty = + DependencyProperty.Register("IsSeparatorVisible", typeof(bool), typeof(RibbonTabItem), new UIPropertyMetadata(false)); + + /// + /// Gets or sets ribbon contextual tab group + /// + public RibbonContextualTabGroup Group + { + get { return (RibbonContextualTabGroup)this.GetValue(GroupProperty); } + set { this.SetValue(GroupProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Group. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty GroupProperty = + DependencyProperty.Register("Group", typeof(RibbonContextualTabGroup), typeof(RibbonTabItem), new UIPropertyMetadata(null, OnGroupChanged)); + + // handles Group property chanhged + private static void OnGroupChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var tab = (RibbonTabItem)d; + + if (e.OldValue != null) + { + ((RibbonContextualTabGroup)e.OldValue).RemoveTabItem(tab); + } + + if (e.NewValue != null) + { + var tabGroup = (RibbonContextualTabGroup)e.NewValue; + tabGroup.AppendTabItem(tab); + tab.IsContextual = true; + } + else + { + tab.IsContextual = false; + } + } + + /// + /// Gets or sets desired width of the tab item + /// + internal double DesiredWidth + { + get { return this.desiredWidth; } + set + { + this.desiredWidth = value; + this.InvalidateMeasure(); + } + } + + /// + /// Gets or sets whether tab item has left group border + /// + public bool HasLeftGroupBorder + { + get { return (bool)this.GetValue(HasLeftGroupBorderProperty); } + set { this.SetValue(HasLeftGroupBorderProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for HaseLeftGroupBorder. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty HasLeftGroupBorderProperty = + DependencyProperty.Register("HasLeftGroupBorder", typeof(bool), typeof(RibbonTabItem), new UIPropertyMetadata(false)); + + /// + /// Gets or sets whether tab item has right group border + /// + public bool HasRightGroupBorder + { + get { return (bool)this.GetValue(HasRightGroupBorderProperty); } + set { this.SetValue(HasRightGroupBorderProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for HaseLeftGroupBorder. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty HasRightGroupBorderProperty = + DependencyProperty.Register("HasRightGroupBorder", typeof(bool), typeof(RibbonTabItem), new UIPropertyMetadata(false)); + + /// + /// get collection of ribbon groups + /// + public ObservableCollection Groups + { + get + { + if (this.groups == null) + { + this.groups = new ObservableCollection(); + this.groups.CollectionChanged += this.OnGroupsCollectionChanged; + } + return this.groups; + } + } + + // handles ribbon groups collection changes + private void OnGroupsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + if (this.groupsInnerContainer == null) + { + return; + } + + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + for (int i = 0; i < e.NewItems.Count; i++) + { + this.groupsInnerContainer.Children.Insert(e.NewStartingIndex + i, (UIElement)e.NewItems[i]); + } + break; + + case NotifyCollectionChangedAction.Remove: + foreach (var item in e.OldItems.OfType()) + { + this.groupsInnerContainer.Children.Remove(item); + } + break; + + case NotifyCollectionChangedAction.Replace: + foreach (var item in e.OldItems.OfType()) + { + this.groupsInnerContainer.Children.Remove(item); + } + foreach (var item in e.NewItems.OfType()) + { + this.groupsInnerContainer.Children.Add(item); + } + break; + + case NotifyCollectionChangedAction.Reset: + this.groupsInnerContainer.Children.Clear(); + + foreach (var group in this.groups) + { + this.groupsInnerContainer.Children.Add(group); + } + break; + } + + } + + #region Header Property + + /// + /// Gets or sets header of tab item + /// + public object Header + { + get { return (object)this.GetValue(HeaderProperty); } + set { this.SetValue(HeaderProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Header. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty HeaderProperty = + DependencyProperty.Register("Header", typeof(object), typeof(RibbonTabItem), new UIPropertyMetadata(null, OnHeaderChanged)); + + // Header changed handler + static void OnHeaderChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + RibbonTabItem tabItem = (RibbonTabItem)d; + tabItem.CoerceValue(ToolTipProperty); + } + + #endregion + + #region Focusable + + /// + /// Handles IsEnabled changes + /// + /// + /// The event data. + private static void OnFocusableChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + } + + /// + /// Coerces IsEnabled + /// + /// + /// + /// + private static object CoerceFocusable(DependencyObject d, object basevalue) + { + var control = d as RibbonTabItem; + if (control != null) + { + var ribbon = control.FindParentRibbon(); + if (ribbon != null) + { + return ((bool)basevalue) + && ribbon.Focusable; + } + } + + return basevalue; + } + + // Find parent ribbon + private Ribbon FindParentRibbon() + { + var element = this.Parent; + while (element != null) + { + var ribbon = element as Ribbon; + if (ribbon != null) + { + return ribbon; + } + + element = VisualTreeHelper.GetParent(element); + } + + return null; + } + + #endregion + + #endregion + + #region Initialize + + /// + /// Static constructor + /// + [SuppressMessage("Microsoft.Performance", "CA1810")] + static RibbonTabItem() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(RibbonTabItem), new FrameworkPropertyMetadata(typeof(RibbonTabItem))); + FocusableProperty.AddOwner(typeof(RibbonTabItem), new FrameworkPropertyMetadata(OnFocusableChanged, CoerceFocusable)); + ToolTipProperty.OverrideMetadata(typeof(RibbonTabItem), new FrameworkPropertyMetadata(null, CoerceToolTip)); + VisibilityProperty.AddOwner(typeof(RibbonTabItem), new FrameworkPropertyMetadata(OnVisibilityChanged)); + StyleProperty.OverrideMetadata(typeof(RibbonTabItem), new FrameworkPropertyMetadata(null, OnCoerceStyle)); + } + + // Coerce object style + static object OnCoerceStyle(DependencyObject d, object basevalue) + { + if (basevalue == null) + { + basevalue = (d as FrameworkElement).TryFindResource(typeof(RibbonTabItem)); + } + + return basevalue; + } + + // Handles visibility changes + private static void OnVisibilityChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var item = d as RibbonTabItem; + + if (item == null) + { + return; + } + + if (item.Group != null) + { + item.Group.UpdateInnerVisiblityAndGroupBorders(); + } + + if (item.IsSelected + && (Visibility)e.NewValue == Visibility.Collapsed) + { + if (item.TabControlParent != null) + { + if (item.TabControlParent.IsMinimized) + { + item.IsSelected = false; + } + else + { + item.TabControlParent.SelectedItem = item.TabControlParent.GetFirstVisibleItem(); + } + } + } + } + + // Coerce ToolTip to ensure that tooltip displays name of the tabitem + private static object CoerceToolTip(DependencyObject d, object basevalue) + { + var tabItem = (RibbonTabItem)d; + if (basevalue == null + && tabItem.Header is string) + { + basevalue = tabItem.Header; + } + + return basevalue; + } + + /// + /// Default constructor + /// + public RibbonTabItem() + { + this.AddLogicalChild(this.groupsContainer); + this.groupsContainer.Content = this.groupsInnerContainer; + + // Force redirection of DataContext. This is needed, because we detach the container from the visual tree and attach it to a diffrent one (the popup/dropdown) when the ribbon is minimized. + this.groupsInnerContainer.SetBinding(DataContextProperty, new Binding("DataContext") + { + Source = this + }); + + ContextMenuService.Coerce(this); + + this.Loaded += this.OnLoaded; + this.Unloaded += this.OnUnloaded; + } + + #endregion + + #region Overrides + + /// + /// Called to remeasure a control. + /// + /// The maximum size that the method can return. + /// The size of the control, up to the maximum specified by constraint. + protected override Size MeasureOverride(Size constraint) + { + if (this.contentContainer == null) + { + return base.MeasureOverride(constraint); + } + + if (this.IsContextual && this.Group != null && this.Group.Visibility == Visibility.Collapsed) + { + return Size.Empty; + } + + this.contentContainer.Padding = new Thickness(this.Indent, this.contentContainer.Padding.Top, this.Indent, this.contentContainer.Padding.Bottom); + Size baseConstraint = base.MeasureOverride(constraint); + double totalWidth = this.contentContainer.DesiredSize.Width - this.contentContainer.Margin.Left - this.contentContainer.Margin.Right; + (this.contentContainer.Child).Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); + double headerWidth = this.contentContainer.Child.DesiredSize.Width; + if (totalWidth < headerWidth + this.Indent * 2) + { + double newPaddings = Math.Max(0, (totalWidth - headerWidth) / 2); + this.contentContainer.Padding = new Thickness(newPaddings, this.contentContainer.Padding.Top, newPaddings, this.contentContainer.Padding.Bottom); + } + else + { + if (this.desiredWidth != 0) + { + // If header width is larger then tab increase tab width + if ((constraint.Width > this.desiredWidth) && (this.desiredWidth > totalWidth)) baseConstraint.Width = this.desiredWidth; + else + baseConstraint.Width = headerWidth + this.Indent * 2 + this.contentContainer.Margin.Left + this.contentContainer.Margin.Right; + } + } + + if ((this.cachedWidth != baseConstraint.Width) && (this.IsContextual) && (this.Group != null)) + { + this.cachedWidth = baseConstraint.Width; + FrameworkElement parent = (VisualTreeHelper.GetParent(this.Group) as FrameworkElement); + if (parent != null) parent.InvalidateMeasure(); + } + + return baseConstraint; + } + + /// + /// On new style applying + /// + public override void OnApplyTemplate() + { + this.contentContainer = this.GetTemplateChild("PART_ContentContainer") as Border; + } + + /// + /// Invoked when an unhandled System.Windows.UIElement.MouseLeftButtonDownrouted event is raised + /// on this element. Implement this method to add class handling for this event. + /// + /// The System.Windows.Input.MouseButtonEventArgs that contains the event data. + /// The event data reports that the left mouse button was pressed. + protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) + { + if (e.Source == this + && e.ClickCount == 2) + { + e.Handled = true; + + if (this.TabControlParent != null) + { + this.TabControlParent.IsMinimized = !this.TabControlParent.IsMinimized; + } + } + else if (e.Source == this + || !this.IsSelected) + { + if (this.Visibility == Visibility.Visible) + { + if (this.TabControlParent != null) + { + var newItem = this.TabControlParent.ItemContainerGenerator.ItemFromContainer(this); + + if (this.TabControlParent.SelectedTabItem == newItem) + { + this.TabControlParent.IsDropDownOpen = !this.TabControlParent.IsDropDownOpen; + } + else + { + this.TabControlParent.SelectedItem = newItem; + } + + this.TabControlParent.RaiseRequestBackstageClose(); + } + else + { + this.IsSelected = true; + } + + e.Handled = true; + } + } + } + + #endregion + + #region Private methods + + // Handles IsSelected property changes + private static void OnIsSelectedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + RibbonTabItem container = d as RibbonTabItem; + bool newValue = (bool)e.NewValue; + if (newValue) + { + if ((container.TabControlParent != null) && (container.TabControlParent.SelectedItem is RibbonTabItem) && (container.TabControlParent.SelectedItem != container)) + (container.TabControlParent.SelectedItem as RibbonTabItem).IsSelected = false; + container.OnSelected(new RoutedEventArgs(Selector.SelectedEvent, container)); + } + else + { + container.OnUnselected(new RoutedEventArgs(Selector.UnselectedEvent, container)); + } + + } + /// + /// Handles selected + /// + /// The event data + protected virtual void OnSelected(RoutedEventArgs e) + { + this.HandleIsSelectedChanged(e); + } + /// + /// handles unselected + /// + /// The event data + protected virtual void OnUnselected(RoutedEventArgs e) + { + this.HandleIsSelectedChanged(e); + } + + #endregion + + #region Event handling + + // Handles IsSelected property changes + private void HandleIsSelectedChanged(RoutedEventArgs e) + { + this.RaiseEvent(e); + } + + private void OnLoaded(object sender, RoutedEventArgs e) + { + this.SubscribeEvents(); + } + + private void OnUnloaded(object sender, RoutedEventArgs e) + { + this.UnSubscribeEvents(); + } + + private void SubscribeEvents() + { + // Always unsubscribe events to ensure we don't subscribe twice + this.UnSubscribeEvents(); + + if (this.groups != null) + { + this.groups.CollectionChanged += this.OnGroupsCollectionChanged; + } + } + + private void UnSubscribeEvents() + { + if (this.groups != null) + { + this.groups.CollectionChanged -= this.OnGroupsCollectionChanged; + } + } + + #endregion + + /// + /// Handles key tip pressed + /// + public void OnKeyTipPressed() + { + if (this.TabControlParent != null) + { + var currentSelectedItem = this.TabControlParent.SelectedItem as RibbonTabItem; + + if (currentSelectedItem != null) + { + currentSelectedItem.IsSelected = false; + } + } + + this.IsSelected = true; + } + + /// + /// Handles back navigation with KeyTips + /// + public void OnKeyTipBack() + { + if (this.TabControlParent != null + && this.TabControlParent.IsMinimized) + { + this.TabControlParent.IsDropDownOpen = false; + } + } + } } \ No newline at end of file diff --git a/Fluent/Controls/RibbonTabsContainer.cs b/Fluent.Ribbon/Controls/RibbonTabsContainer.cs similarity index 97% rename from Fluent/Controls/RibbonTabsContainer.cs rename to Fluent.Ribbon/Controls/RibbonTabsContainer.cs index 9ed6b6f4d..c8ab6a116 100644 --- a/Fluent/Controls/RibbonTabsContainer.cs +++ b/Fluent.Ribbon/Controls/RibbonTabsContainer.cs @@ -1,756 +1,756 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Controls.Primitives; -using System.Windows.Media; - -namespace Fluent -{ - using Fluent.Internal; - - /// - /// Represent panel with ribbon tab items. - /// It is automatically adjusting size of tabs - /// - public class RibbonTabsContainer : Panel, IScrollInfo - { - /// - /// Default constructor - /// - public RibbonTabsContainer() - { - this.Focusable = false; - } - - #region Layout Overridings - - /// - /// Measures all of the RibbonGroupBox, and resize them appropriately - /// to fit within the available room - /// - /// The available size that this element can give to child elements. - /// The size that the groups container determines it needs during - /// layout, based on its calculations of child element sizes. - /// - [SuppressMessage("Microsoft.Maintainability", "CA1502")] - protected override Size MeasureOverride(Size availableSize) - { - if (this.InternalChildren.Count == 0) return base.MeasureOverride(availableSize); - - Size desiredSize = this.MeasureChildrenDesiredSize(availableSize); - - // Performs steps as described in "2007 MICROSOFT OFFICE FLUENT - // USER INTERFACE DESIGN GUIDELINES" - - // Step 1. Gradually remove empty space to the right of the tabs - // If all tabs already in full size, just return - if (availableSize.Width > desiredSize.Width) - { - // Hide separator lines between tabs - this.UpdateSeparators(false, false); - this.VerifyScrollData(availableSize.Width, desiredSize.Width); - return desiredSize; - } - - // Step 2. Gradually and uniformly remove the padding from both sides - // of all the tabs until the minimum padding required for displaying - // the tab selection and hover states is reached (regular tabs) - double overflowWidth = desiredSize.Width - availableSize.Width; - double whitespace = (this.InternalChildren[0] as RibbonTabItem).Indent; - RibbonTabItem[] contextualTabs = this.InternalChildren.Cast().Where(x => (x.IsContextual) && (x.Visibility != Visibility.Collapsed) && (x.Group.Visibility != Visibility.Collapsed)).ToArray(); - double contextualTabsCount = contextualTabs.Length; - var regularTabs = this.InternalChildren.Cast().Where(x => (!x.IsContextual) && (x.Visibility != Visibility.Collapsed)); - double regularTabsCount = regularTabs.Count();//InternalChildren.Count - contextualTabsCount; - double childrenCount = contextualTabsCount + regularTabsCount; - if (overflowWidth < regularTabsCount * whitespace * 2) - { - double decreaseValue = overflowWidth / (double)regularTabsCount; - foreach (RibbonTabItem tab in regularTabs) tab.Measure(new Size(Math.Max(0, tab.DesiredSize.Width - decreaseValue), tab.DesiredSize.Height));// tab.Width = Math.Max(0, tab.ActualWidth - decreaseValue); - desiredSize = this.GetChildrenDesiredSize(); - if (desiredSize.Width > availableSize.Width) desiredSize.Width = availableSize.Width; - - // Add separator lines between - // tabs to assist readability - this.UpdateSeparators(false, false); - this.VerifyScrollData(availableSize.Width, desiredSize.Width); - return desiredSize; - } - - // Step 3. Gradually and uniformly remove the padding from both sides - // of all the tabs until the minimum padding required for displaying - // the tab selection and hover states is reached (contextual tabs) - if (overflowWidth < childrenCount * whitespace * 2) - { - double regularTabsWhitespace = (double)regularTabsCount * whitespace * 2.0; - double decreaseValue = (overflowWidth - regularTabsWhitespace) / (double)contextualTabsCount; - foreach (RibbonTabItem tab in regularTabs) - { - //if (!tab.IsContextual) - { - double widthBeforeMeasure = tab.DesiredSize.Width; - tab.Measure(new Size(Math.Max(0, tab.DesiredSize.Width - whitespace * 2.0), tab.DesiredSize.Height)); - overflowWidth -= widthBeforeMeasure - tab.DesiredSize.Width; - } - } - foreach (RibbonTabItem tab in contextualTabs.Reverse()) - { - //if (tab.IsContextual) - { - double widthBeforeMeasure = tab.DesiredSize.Width; - tab.Measure(new Size(Math.Max(0, tab.DesiredSize.Width - decreaseValue), tab.DesiredSize.Height)); - - // Contextual tabs may overreduce, so check that - overflowWidth -= widthBeforeMeasure - tab.DesiredSize.Width; - if (overflowWidth < 0) break; - } - } - desiredSize = this.GetChildrenDesiredSize(); - if (desiredSize.Width > availableSize.Width) desiredSize.Width = availableSize.Width; - - // Add separator lines between - // tabs to assist readability - this.UpdateSeparators(true, false); - this.VerifyScrollData(availableSize.Width, desiredSize.Width); - return desiredSize; - } - - - // Step 4. Reduce the width of the tab with the longest name by - // truncating the text label. Continue reducing the width of the largest - // tab (or tabs in the case of ties) until all tabs are the same width. - // (Regular tabs) - foreach (RibbonTabItem tab in regularTabs) - { - //if (!tab.IsContextual) - { - double widthBeforeMeasure = tab.DesiredSize.Width; - tab.Measure(new Size(Math.Max(0, tab.DesiredSize.Width - whitespace * 2.0), tab.DesiredSize.Height)); - overflowWidth -= widthBeforeMeasure - tab.DesiredSize.Width; - } - } - foreach (RibbonTabItem tab in contextualTabs.Reverse()) - { - //if (tab.IsContextual) - { - double widthBeforeMeasure = tab.DesiredSize.Width; - tab.Measure(new Size(Math.Max(0, tab.DesiredSize.Width - whitespace * 2.0), tab.DesiredSize.Height)); - - // Contextual tabs may overreduce, so check that - overflowWidth -= widthBeforeMeasure - tab.DesiredSize.Width; - if (overflowWidth < 0) - { - desiredSize = this.GetChildrenDesiredSize(); - if (desiredSize.Width > availableSize.Width) desiredSize.Width = availableSize.Width; - - // Add separator lines between - // tabs to assist readability - this.UpdateSeparators(true, false); - this.VerifyScrollData(availableSize.Width, desiredSize.Width); - return desiredSize; - } - } - } - - // Sort regular tabs by descending - RibbonTabItem[] sortedRegularTabItems = regularTabs - .OrderByDescending(x => x.DesiredSize.Width) - .ToArray(); - - // Find how many regular tabs we have to reduce - double reducedLength = 0; - int reduceCount = 0; - for (int i = 0; i < sortedRegularTabItems.Length - 1; i++) - { - double temp = - sortedRegularTabItems[i].DesiredSize.Width - - sortedRegularTabItems[i + 1].DesiredSize.Width; - reducedLength += temp * (i + 1); - reduceCount = i + 1; - if (reducedLength > overflowWidth) break; - } - - if (reducedLength > overflowWidth) - { - // Reduce regular tabs - double requiredWidth = sortedRegularTabItems[reduceCount].DesiredSize.Width; - if (reducedLength > overflowWidth) requiredWidth += (reducedLength - overflowWidth) / (double)reduceCount; - for (int i = 0; i < reduceCount; i++) - { - sortedRegularTabItems[i].Measure(new Size(requiredWidth, availableSize.Height)); - } - - desiredSize = this.GetChildrenDesiredSize(); - if (desiredSize.Width > availableSize.Width) desiredSize.Width = availableSize.Width; - - // Add separator lines between - // tabs to assist readability - this.UpdateSeparators(true, true); - this.VerifyScrollData(availableSize.Width, desiredSize.Width); - return desiredSize; - } - - - // Step 5. Reduce the width of all regular tabs equally - // down to a minimum of about three characters. - double regularTabsWidth = sortedRegularTabItems.Sum(x => x.DesiredSize.Width); - double minimumRegularTabsWidth = MinimumRegularTabWidth * sortedRegularTabItems.Length; - - if (overflowWidth < regularTabsWidth - minimumRegularTabsWidth) - { - double settedWidth = (regularTabsWidth - overflowWidth) / (double)regularTabsCount; - for (int i = 0; i < regularTabsCount; i++) - { - sortedRegularTabItems[i].Measure(new Size(settedWidth, availableSize.Height)); - } - desiredSize = this.GetChildrenDesiredSize(); - //if (desiredSize.Width > availableSize.Width) desiredSize.Width = availableSize.Width; - - // Add separator lines between - // tabs to assist readability - this.UpdateSeparators(true, true); - this.VerifyScrollData(availableSize.Width, desiredSize.Width); - return desiredSize; - } - - // Step 6. Reduce the width of the tab with the longest name by - // truncating the text label. Continue reducing the width of the largest - // tab (or tabs in the case of ties) until all tabs are the same width. - // (Contextual tabs) - for (int i = 0; i < regularTabsCount; i++) - { - sortedRegularTabItems[i].Measure(new Size(MinimumRegularTabWidth, availableSize.Height)); - } - overflowWidth -= regularTabsWidth - minimumRegularTabsWidth; - - // Sort contextual tabs by descending - RibbonTabItem[] sortedContextualTabItems = contextualTabs - .OrderByDescending(x => x.DesiredSize.Width) - .ToArray(); - - // Find how many contextual tabs we have to reduce - reducedLength = 0; - reduceCount = 0; - for (int i = 0; i < sortedContextualTabItems.Length - 1; i++) - { - double temp = - sortedContextualTabItems[i].DesiredSize.Width - - sortedContextualTabItems[i + 1].DesiredSize.Width; - reducedLength += temp * (i + 1); - reduceCount = i + 1; - if (reducedLength > overflowWidth) break; - } - - if (reducedLength > overflowWidth) - { - // Reduce regular tabs - double requiredWidth = sortedContextualTabItems[reduceCount].DesiredSize.Width; - if (reducedLength > overflowWidth) requiredWidth += (reducedLength - overflowWidth) / (double)reduceCount; - for (int i = 0; i < reduceCount; i++) - { - sortedContextualTabItems[i].Measure(new Size(requiredWidth, availableSize.Height)); - } - - desiredSize = this.GetChildrenDesiredSize(); - if (desiredSize.Width > availableSize.Width) desiredSize.Width = availableSize.Width; - - // Add separator lines between - // tabs to assist readability - this.UpdateSeparators(true, true); - this.VerifyScrollData(availableSize.Width, desiredSize.Width); - return desiredSize; - } - else - { - double contextualTabsWidth = sortedContextualTabItems.Sum(x => x.DesiredSize.Width); - - double settedWidth = Math.Max(MinimumRegularTabWidth, (contextualTabsWidth - overflowWidth) / (double)contextualTabsCount); - for (int i = 0; i < sortedContextualTabItems.Length; i++) - { - sortedContextualTabItems[i].Measure(new Size(settedWidth, availableSize.Height)); - } - desiredSize = this.GetChildrenDesiredSize(); - - // Add separator lines between - // tabs to assist readability - this.UpdateSeparators(true, true); - this.VerifyScrollData(availableSize.Width, desiredSize.Width); - return desiredSize; - } - } - - private Size MeasureChildrenDesiredSize(Size availableSize) - { - double width = 0; - double height = 0; - foreach (UIElement child in this.InternalChildren) - { - child.Measure(availableSize); - width += child.DesiredSize.Width; - height = Math.Max(height, child.DesiredSize.Height); - } - return new Size(width, height); - } - - private Size GetChildrenDesiredSize() - { - double width = 0; - double height = 0; - foreach (UIElement child in this.InternalChildren) - { - width += child.DesiredSize.Width; - height = Math.Max(height, child.DesiredSize.Height); - } - return new Size(width, height); - } - - /// - /// Positions child elements and determines - /// a size for the control - /// - /// The final area within the parent - /// that this element should use to arrange - /// itself and its children - /// The actual size used - protected override Size ArrangeOverride(Size finalSize) - { - var finalRect = new Rect(finalSize) - { - X = -this.HorizontalOffset - }; - - var orderedChildren = this.InternalChildren.OfType() - .OrderBy(x => x.Group != null); - - foreach (var item in orderedChildren) - { - finalRect.Width = item.DesiredSize.Width; - finalRect.Height = Math.Max(finalSize.Height, item.DesiredSize.Height); - item.Arrange(finalRect); - finalRect.X += item.DesiredSize.Width; - } - - var ribbonTabItemsWithGroups = this.InternalChildren.OfType() - .Where(item => item.Group != null); - - var ribbonTitleBar = ribbonTabItemsWithGroups.Select(ribbonTabItemsWithGroup => ribbonTabItemsWithGroup.Group.Parent) - .OfType() - .FirstOrDefault(); - - if (ribbonTitleBar != null) - { - ribbonTitleBar.InvalidateMeasure(); - } - - return finalSize; - } - - /// - /// Updates separator visibility - /// - /// If this parameter true, regular tabs will have separators - /// If this parameter true, contextual tabs will have separators - private void UpdateSeparators(bool regularTabs, bool contextualTabs) - { - foreach (RibbonTabItem tab in this.Children) - { - if (tab.IsContextual) - { - if (tab.IsSeparatorVisible != contextualTabs) tab.IsSeparatorVisible = contextualTabs; - } - else if (tab.IsSeparatorVisible != regularTabs) - { - tab.IsSeparatorVisible = regularTabs; - } - } - } - - #endregion - - #region IScrollInfo Members - - /// - /// Gets or sets a System.Windows.Controls.ScrollViewer element that controls scrolling behavior. - /// - public ScrollViewer ScrollOwner - { - get { return this.ScrollData.ScrollOwner; } - set { this.ScrollData.ScrollOwner = value; } - } - - /// - /// Sets the amount of horizontal offset. - /// - /// The degree to which content is horizontally offset from the containing viewport. - public void SetHorizontalOffset(double offset) - { - double newValue = CoerceOffset(ValidateInputOffset(offset, "HorizontalOffset"), this.scrollData.ExtentWidth, this.scrollData.ViewportWidth); - if (DoubleUtil.AreClose(this.ScrollData.OffsetX, newValue) == false) - { - this.scrollData.OffsetX = newValue; - this.InvalidateArrange(); - } - } - - /// - /// Gets the horizontal size of the extent. - /// - public double ExtentWidth - { - get { return this.ScrollData.ExtentWidth; } - } - - /// - /// Gets the horizontal offset of the scrolled content. - /// - public double HorizontalOffset - { - get { return this.ScrollData.OffsetX; } - } - - /// - /// Gets the horizontal size of the viewport for this content. - /// - public double ViewportWidth - { - get { return this.ScrollData.ViewportWidth; } - } - - /// - /// Scrolls left within content by one logical unit. - /// - public void LineLeft() - { - this.SetHorizontalOffset(this.HorizontalOffset - 16.0); - } - - /// - /// Scrolls right within content by one logical unit. - /// - public void LineRight() - { - this.SetHorizontalOffset(this.HorizontalOffset + 16.0); - } - - /// - /// Forces content to scroll until the coordinate space of a System.Windows.Media.Visual object is visible. - /// This is optimized for horizontal scrolling only - /// - /// A System.Windows.Media.Visual that becomes visible. - /// A bounding rectangle that identifies the coordinate space to make visible. - /// A System.Windows.Rect that is visible. - public Rect MakeVisible(Visual visual, Rect rectangle) - { - // We can only work on visuals that are us or children. - // An empty rect has no size or position. We can't meaningfully use it. - if (rectangle.IsEmpty - || visual == null - || visual == (Visual)this - || !this.IsAncestorOf(visual)) - { - return Rect.Empty; - } - - // Compute the child's rect relative to (0,0) in our coordinate space. - GeneralTransform childTransform = visual.TransformToAncestor(this); - - rectangle = childTransform.TransformBounds(rectangle); - - // Initialize the viewport - Rect viewport = new Rect(this.HorizontalOffset, rectangle.Top, this.ViewportWidth, rectangle.Height); - rectangle.X += viewport.X; - - // Compute the offsets required to minimally scroll the child maximally into view. - double minX = ComputeScrollOffsetWithMinimalScroll(viewport.Left, viewport.Right, rectangle.Left, rectangle.Right); - - // We have computed the scrolling offsets; scroll to them. - this.SetHorizontalOffset(minX); - - // Compute the visible rectangle of the child relative to the viewport. - viewport.X = minX; - rectangle.Intersect(viewport); - - rectangle.X -= viewport.X; - - // Return the rectangle - return rectangle; - } - - static double ComputeScrollOffsetWithMinimalScroll( - double topView, - double bottomView, - double topChild, - double bottomChild) - { - // # CHILD POSITION CHILD SIZE SCROLL REMEDY - // 1 Above viewport <= viewport Down Align top edge of child & viewport - // 2 Above viewport > viewport Down Align bottom edge of child & viewport - // 3 Below viewport <= viewport Up Align bottom edge of child & viewport - // 4 Below viewport > viewport Up Align top edge of child & viewport - // 5 Entirely within viewport NA No scroll. - // 6 Spanning viewport NA No scroll. - // - // Note: "Above viewport" = childTop above viewportTop, childBottom above viewportBottom - // "Below viewport" = childTop below viewportTop, childBottom below viewportBottom - // These child thus may overlap with the viewport, but will scroll the same direction - /*bool fAbove = DoubleUtil.LessThan(topChild, topView) && DoubleUtil.LessThan(bottomChild, bottomView); - bool fBelow = DoubleUtil.GreaterThan(bottomChild, bottomView) && DoubleUtil.GreaterThan(topChild, topView);*/ - bool fAbove = (topChild < topView) && (bottomChild < bottomView); - bool fBelow = (bottomChild > bottomView) && (topChild > topView); - bool fLarger = (bottomChild - topChild) > (bottomView - topView); - - // Handle Cases: 1 & 4 above - if ((fAbove && !fLarger) - || (fBelow && fLarger)) - { - return topChild; - } - - // Handle Cases: 2 & 3 above - else if (fAbove || fBelow) - { - return bottomChild - (bottomView - topView); - } - - // Handle cases: 5 & 6 above. - return topView; - } - - /// - /// Not implemented - /// - public void MouseWheelDown() - { - } - /// - /// Not implemented - /// - public void MouseWheelLeft() - { - } - /// - /// Not implemented - /// - public void MouseWheelRight() - { - } - /// - /// Not implemented - /// - public void MouseWheelUp() - { - } - /// - /// Not implemented - /// - public void LineDown() - { - } - /// - /// Not implemented - /// - public void LineUp() - { - } - /// - /// Not implemented - /// - public void PageDown() - { - } - /// - /// Not implemented - /// - public void PageLeft() - { - } - /// - /// Not implemented - /// - public void PageRight() - { - } - /// - /// Not implemented - /// - public void PageUp() - { - } - /// - /// Not implemented - /// - /// - public void SetVerticalOffset(double offset) - { - } - /// - /// Gets or sets a value that indicates whether scrolling on the vertical axis is possible. - /// - public bool CanVerticallyScroll - { - get { return false; } - set { } - } - /// - /// Gets or sets a value that indicates whether scrolling on the horizontal axis is possible. - /// - public bool CanHorizontallyScroll - { - get { return true; } - set { } - } - /// - /// Not implemented - /// - public double ExtentHeight - { - get { return 0.0; } - }/// - /// Not implemented - /// - - public double VerticalOffset - { - get { return 0.0; } - } - /// - /// Not implemented - /// - public double ViewportHeight - { - get { return 0.0; } - } - - // Gets scroll data info - private ScrollData ScrollData - { - get - { - return this.scrollData ?? (this.scrollData = new ScrollData()); - } - } - - // Scroll data info - private ScrollData scrollData; - private const double MinimumRegularTabWidth = 30D; - - // Validates input offset - static double ValidateInputOffset(double offset, string parameterName) - { - if (double.IsNaN(offset)) - { - throw new ArgumentOutOfRangeException(parameterName); - } - - return Math.Max(0.0, offset); - } - - // Verifies scrolling data using the passed viewport and extent as newly computed values. - // Checks the X/Y offset and coerces them into the range [0, Extent - ViewportSize] - // If extent, viewport, or the newly coerced offsets are different than the existing offset, - // cachces are updated and InvalidateScrollInfo() is called. - private void VerifyScrollData(double viewportWidth, double extentWidth) - { - bool isValid = true; - - if (double.IsInfinity(viewportWidth)) - { - viewportWidth = extentWidth; - } - - double offsetX = CoerceOffset(this.ScrollData.OffsetX, extentWidth, viewportWidth); - - isValid &= DoubleUtil.AreClose(viewportWidth, this.ScrollData.ViewportWidth); - isValid &= DoubleUtil.AreClose(extentWidth, this.ScrollData.ExtentWidth); - isValid &= DoubleUtil.AreClose(this.ScrollData.OffsetX, offsetX); - - this.ScrollData.ViewportWidth = viewportWidth; - - // newExtentWidth is neccessary to fix 20762 (Tab scroll button appears randomly when resizing) - // To fix 20762 we are manipulating the extentWidth by checking if all regular (non contextual) tabs are at their minimum width. - // When they are all at their minimum width we have to force the extentWidth to be greater than the viewportWidth. - // When there are no regular tabs, we MUST NOT apply this fix - var newExtentWidth = Math.Max(viewportWidth, extentWidth); - - var visibleRegularTabs = this.InternalChildren.Cast() - .Where(item => item.IsContextual == false && item.Visibility != Visibility.Collapsed) - .ToArray(); - - if (visibleRegularTabs.Any() - && visibleRegularTabs.All(item => DoubleUtil.AreClose(item.DesiredSize.Width, MinimumRegularTabWidth))) - { - if (DoubleUtil.AreClose(newExtentWidth, viewportWidth)) - { - newExtentWidth = newExtentWidth + 1; - } - - this.ScrollData.ExtentWidth = newExtentWidth; - } - else - { - this.ScrollData.ExtentWidth = this.ScrollData.ViewportWidth; - } - - this.ScrollData.OffsetX = offsetX; - - if (!isValid) - { - if (this.ScrollOwner != null) - { - this.ScrollOwner.InvalidateScrollInfo(); - } - } - } - - // Returns an offset coerced into the [0, Extent - Viewport] range. - static double CoerceOffset(double offset, double extent, double viewport) - { - if (offset > extent - viewport) - { - offset = extent - viewport; - } - - if (offset < 0) - { - offset = 0; - } - - return offset; - } - - #endregion - } - - #region ScrollData - - /// - /// Helper class to hold scrolling data. - /// This class exists to reduce working set when SCP is delegating to another implementation of ISI. - /// Standard "extra pointer always for less data sometimes" cache savings model: - /// - internal class ScrollData - { - /// - /// Scroll viewer - /// - internal ScrollViewer ScrollOwner; - - /// - /// Scroll offset - /// - internal double OffsetX; - - /// - /// ViewportSize is computed from our FinalSize, but may be in different units. - /// - internal double ViewportWidth; - - /// - /// Extent is the total size of our content. - /// - internal double ExtentWidth; - } - - #endregion ScrollData +using System; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Media; + +namespace Fluent +{ + using Fluent.Internal; + + /// + /// Represent panel with ribbon tab items. + /// It is automatically adjusting size of tabs + /// + public class RibbonTabsContainer : Panel, IScrollInfo + { + /// + /// Default constructor + /// + public RibbonTabsContainer() + { + this.Focusable = false; + } + + #region Layout Overridings + + /// + /// Measures all of the RibbonGroupBox, and resize them appropriately + /// to fit within the available room + /// + /// The available size that this element can give to child elements. + /// The size that the groups container determines it needs during + /// layout, based on its calculations of child element sizes. + /// + [SuppressMessage("Microsoft.Maintainability", "CA1502")] + protected override Size MeasureOverride(Size availableSize) + { + if (this.InternalChildren.Count == 0) return base.MeasureOverride(availableSize); + + Size desiredSize = this.MeasureChildrenDesiredSize(availableSize); + + // Performs steps as described in "2007 MICROSOFT OFFICE FLUENT + // USER INTERFACE DESIGN GUIDELINES" + + // Step 1. Gradually remove empty space to the right of the tabs + // If all tabs already in full size, just return + if (availableSize.Width > desiredSize.Width) + { + // Hide separator lines between tabs + this.UpdateSeparators(false, false); + this.VerifyScrollData(availableSize.Width, desiredSize.Width); + return desiredSize; + } + + // Step 2. Gradually and uniformly remove the padding from both sides + // of all the tabs until the minimum padding required for displaying + // the tab selection and hover states is reached (regular tabs) + double overflowWidth = desiredSize.Width - availableSize.Width; + double whitespace = (this.InternalChildren[0] as RibbonTabItem).Indent; + RibbonTabItem[] contextualTabs = this.InternalChildren.Cast().Where(x => (x.IsContextual) && (x.Visibility != Visibility.Collapsed) && (x.Group.Visibility != Visibility.Collapsed)).ToArray(); + double contextualTabsCount = contextualTabs.Length; + var regularTabs = this.InternalChildren.Cast().Where(x => (!x.IsContextual) && (x.Visibility != Visibility.Collapsed)); + double regularTabsCount = regularTabs.Count();//InternalChildren.Count - contextualTabsCount; + double childrenCount = contextualTabsCount + regularTabsCount; + if (overflowWidth < regularTabsCount * whitespace * 2) + { + double decreaseValue = overflowWidth / (double)regularTabsCount; + foreach (RibbonTabItem tab in regularTabs) tab.Measure(new Size(Math.Max(0, tab.DesiredSize.Width - decreaseValue), tab.DesiredSize.Height));// tab.Width = Math.Max(0, tab.ActualWidth - decreaseValue); + desiredSize = this.GetChildrenDesiredSize(); + if (desiredSize.Width > availableSize.Width) desiredSize.Width = availableSize.Width; + + // Add separator lines between + // tabs to assist readability + this.UpdateSeparators(false, false); + this.VerifyScrollData(availableSize.Width, desiredSize.Width); + return desiredSize; + } + + // Step 3. Gradually and uniformly remove the padding from both sides + // of all the tabs until the minimum padding required for displaying + // the tab selection and hover states is reached (contextual tabs) + if (overflowWidth < childrenCount * whitespace * 2) + { + double regularTabsWhitespace = (double)regularTabsCount * whitespace * 2.0; + double decreaseValue = (overflowWidth - regularTabsWhitespace) / (double)contextualTabsCount; + foreach (RibbonTabItem tab in regularTabs) + { + //if (!tab.IsContextual) + { + double widthBeforeMeasure = tab.DesiredSize.Width; + tab.Measure(new Size(Math.Max(0, tab.DesiredSize.Width - whitespace * 2.0), tab.DesiredSize.Height)); + overflowWidth -= widthBeforeMeasure - tab.DesiredSize.Width; + } + } + foreach (RibbonTabItem tab in contextualTabs.Reverse()) + { + //if (tab.IsContextual) + { + double widthBeforeMeasure = tab.DesiredSize.Width; + tab.Measure(new Size(Math.Max(0, tab.DesiredSize.Width - decreaseValue), tab.DesiredSize.Height)); + + // Contextual tabs may overreduce, so check that + overflowWidth -= widthBeforeMeasure - tab.DesiredSize.Width; + if (overflowWidth < 0) break; + } + } + desiredSize = this.GetChildrenDesiredSize(); + if (desiredSize.Width > availableSize.Width) desiredSize.Width = availableSize.Width; + + // Add separator lines between + // tabs to assist readability + this.UpdateSeparators(true, false); + this.VerifyScrollData(availableSize.Width, desiredSize.Width); + return desiredSize; + } + + + // Step 4. Reduce the width of the tab with the longest name by + // truncating the text label. Continue reducing the width of the largest + // tab (or tabs in the case of ties) until all tabs are the same width. + // (Regular tabs) + foreach (RibbonTabItem tab in regularTabs) + { + //if (!tab.IsContextual) + { + double widthBeforeMeasure = tab.DesiredSize.Width; + tab.Measure(new Size(Math.Max(0, tab.DesiredSize.Width - whitespace * 2.0), tab.DesiredSize.Height)); + overflowWidth -= widthBeforeMeasure - tab.DesiredSize.Width; + } + } + foreach (RibbonTabItem tab in contextualTabs.Reverse()) + { + //if (tab.IsContextual) + { + double widthBeforeMeasure = tab.DesiredSize.Width; + tab.Measure(new Size(Math.Max(0, tab.DesiredSize.Width - whitespace * 2.0), tab.DesiredSize.Height)); + + // Contextual tabs may overreduce, so check that + overflowWidth -= widthBeforeMeasure - tab.DesiredSize.Width; + if (overflowWidth < 0) + { + desiredSize = this.GetChildrenDesiredSize(); + if (desiredSize.Width > availableSize.Width) desiredSize.Width = availableSize.Width; + + // Add separator lines between + // tabs to assist readability + this.UpdateSeparators(true, false); + this.VerifyScrollData(availableSize.Width, desiredSize.Width); + return desiredSize; + } + } + } + + // Sort regular tabs by descending + RibbonTabItem[] sortedRegularTabItems = regularTabs + .OrderByDescending(x => x.DesiredSize.Width) + .ToArray(); + + // Find how many regular tabs we have to reduce + double reducedLength = 0; + int reduceCount = 0; + for (int i = 0; i < sortedRegularTabItems.Length - 1; i++) + { + double temp = + sortedRegularTabItems[i].DesiredSize.Width - + sortedRegularTabItems[i + 1].DesiredSize.Width; + reducedLength += temp * (i + 1); + reduceCount = i + 1; + if (reducedLength > overflowWidth) break; + } + + if (reducedLength > overflowWidth) + { + // Reduce regular tabs + double requiredWidth = sortedRegularTabItems[reduceCount].DesiredSize.Width; + if (reducedLength > overflowWidth) requiredWidth += (reducedLength - overflowWidth) / (double)reduceCount; + for (int i = 0; i < reduceCount; i++) + { + sortedRegularTabItems[i].Measure(new Size(requiredWidth, availableSize.Height)); + } + + desiredSize = this.GetChildrenDesiredSize(); + if (desiredSize.Width > availableSize.Width) desiredSize.Width = availableSize.Width; + + // Add separator lines between + // tabs to assist readability + this.UpdateSeparators(true, true); + this.VerifyScrollData(availableSize.Width, desiredSize.Width); + return desiredSize; + } + + + // Step 5. Reduce the width of all regular tabs equally + // down to a minimum of about three characters. + double regularTabsWidth = sortedRegularTabItems.Sum(x => x.DesiredSize.Width); + double minimumRegularTabsWidth = MinimumRegularTabWidth * sortedRegularTabItems.Length; + + if (overflowWidth < regularTabsWidth - minimumRegularTabsWidth) + { + double settedWidth = (regularTabsWidth - overflowWidth) / (double)regularTabsCount; + for (int i = 0; i < regularTabsCount; i++) + { + sortedRegularTabItems[i].Measure(new Size(settedWidth, availableSize.Height)); + } + desiredSize = this.GetChildrenDesiredSize(); + //if (desiredSize.Width > availableSize.Width) desiredSize.Width = availableSize.Width; + + // Add separator lines between + // tabs to assist readability + this.UpdateSeparators(true, true); + this.VerifyScrollData(availableSize.Width, desiredSize.Width); + return desiredSize; + } + + // Step 6. Reduce the width of the tab with the longest name by + // truncating the text label. Continue reducing the width of the largest + // tab (or tabs in the case of ties) until all tabs are the same width. + // (Contextual tabs) + for (int i = 0; i < regularTabsCount; i++) + { + sortedRegularTabItems[i].Measure(new Size(MinimumRegularTabWidth, availableSize.Height)); + } + overflowWidth -= regularTabsWidth - minimumRegularTabsWidth; + + // Sort contextual tabs by descending + RibbonTabItem[] sortedContextualTabItems = contextualTabs + .OrderByDescending(x => x.DesiredSize.Width) + .ToArray(); + + // Find how many contextual tabs we have to reduce + reducedLength = 0; + reduceCount = 0; + for (int i = 0; i < sortedContextualTabItems.Length - 1; i++) + { + double temp = + sortedContextualTabItems[i].DesiredSize.Width - + sortedContextualTabItems[i + 1].DesiredSize.Width; + reducedLength += temp * (i + 1); + reduceCount = i + 1; + if (reducedLength > overflowWidth) break; + } + + if (reducedLength > overflowWidth) + { + // Reduce regular tabs + double requiredWidth = sortedContextualTabItems[reduceCount].DesiredSize.Width; + if (reducedLength > overflowWidth) requiredWidth += (reducedLength - overflowWidth) / (double)reduceCount; + for (int i = 0; i < reduceCount; i++) + { + sortedContextualTabItems[i].Measure(new Size(requiredWidth, availableSize.Height)); + } + + desiredSize = this.GetChildrenDesiredSize(); + if (desiredSize.Width > availableSize.Width) desiredSize.Width = availableSize.Width; + + // Add separator lines between + // tabs to assist readability + this.UpdateSeparators(true, true); + this.VerifyScrollData(availableSize.Width, desiredSize.Width); + return desiredSize; + } + else + { + double contextualTabsWidth = sortedContextualTabItems.Sum(x => x.DesiredSize.Width); + + double settedWidth = Math.Max(MinimumRegularTabWidth, (contextualTabsWidth - overflowWidth) / (double)contextualTabsCount); + for (int i = 0; i < sortedContextualTabItems.Length; i++) + { + sortedContextualTabItems[i].Measure(new Size(settedWidth, availableSize.Height)); + } + desiredSize = this.GetChildrenDesiredSize(); + + // Add separator lines between + // tabs to assist readability + this.UpdateSeparators(true, true); + this.VerifyScrollData(availableSize.Width, desiredSize.Width); + return desiredSize; + } + } + + private Size MeasureChildrenDesiredSize(Size availableSize) + { + double width = 0; + double height = 0; + foreach (UIElement child in this.InternalChildren) + { + child.Measure(availableSize); + width += child.DesiredSize.Width; + height = Math.Max(height, child.DesiredSize.Height); + } + return new Size(width, height); + } + + private Size GetChildrenDesiredSize() + { + double width = 0; + double height = 0; + foreach (UIElement child in this.InternalChildren) + { + width += child.DesiredSize.Width; + height = Math.Max(height, child.DesiredSize.Height); + } + return new Size(width, height); + } + + /// + /// Positions child elements and determines + /// a size for the control + /// + /// The final area within the parent + /// that this element should use to arrange + /// itself and its children + /// The actual size used + protected override Size ArrangeOverride(Size finalSize) + { + var finalRect = new Rect(finalSize) + { + X = -this.HorizontalOffset + }; + + var orderedChildren = this.InternalChildren.OfType() + .OrderBy(x => x.Group != null); + + foreach (var item in orderedChildren) + { + finalRect.Width = item.DesiredSize.Width; + finalRect.Height = Math.Max(finalSize.Height, item.DesiredSize.Height); + item.Arrange(finalRect); + finalRect.X += item.DesiredSize.Width; + } + + var ribbonTabItemsWithGroups = this.InternalChildren.OfType() + .Where(item => item.Group != null); + + var ribbonTitleBar = ribbonTabItemsWithGroups.Select(ribbonTabItemsWithGroup => ribbonTabItemsWithGroup.Group.Parent) + .OfType() + .FirstOrDefault(); + + if (ribbonTitleBar != null) + { + ribbonTitleBar.InvalidateMeasure(); + } + + return finalSize; + } + + /// + /// Updates separator visibility + /// + /// If this parameter true, regular tabs will have separators + /// If this parameter true, contextual tabs will have separators + private void UpdateSeparators(bool regularTabs, bool contextualTabs) + { + foreach (RibbonTabItem tab in this.Children) + { + if (tab.IsContextual) + { + if (tab.IsSeparatorVisible != contextualTabs) tab.IsSeparatorVisible = contextualTabs; + } + else if (tab.IsSeparatorVisible != regularTabs) + { + tab.IsSeparatorVisible = regularTabs; + } + } + } + + #endregion + + #region IScrollInfo Members + + /// + /// Gets or sets a System.Windows.Controls.ScrollViewer element that controls scrolling behavior. + /// + public ScrollViewer ScrollOwner + { + get { return this.ScrollData.ScrollOwner; } + set { this.ScrollData.ScrollOwner = value; } + } + + /// + /// Sets the amount of horizontal offset. + /// + /// The degree to which content is horizontally offset from the containing viewport. + public void SetHorizontalOffset(double offset) + { + double newValue = CoerceOffset(ValidateInputOffset(offset, "HorizontalOffset"), this.scrollData.ExtentWidth, this.scrollData.ViewportWidth); + if (DoubleUtil.AreClose(this.ScrollData.OffsetX, newValue) == false) + { + this.scrollData.OffsetX = newValue; + this.InvalidateArrange(); + } + } + + /// + /// Gets the horizontal size of the extent. + /// + public double ExtentWidth + { + get { return this.ScrollData.ExtentWidth; } + } + + /// + /// Gets the horizontal offset of the scrolled content. + /// + public double HorizontalOffset + { + get { return this.ScrollData.OffsetX; } + } + + /// + /// Gets the horizontal size of the viewport for this content. + /// + public double ViewportWidth + { + get { return this.ScrollData.ViewportWidth; } + } + + /// + /// Scrolls left within content by one logical unit. + /// + public void LineLeft() + { + this.SetHorizontalOffset(this.HorizontalOffset - 16.0); + } + + /// + /// Scrolls right within content by one logical unit. + /// + public void LineRight() + { + this.SetHorizontalOffset(this.HorizontalOffset + 16.0); + } + + /// + /// Forces content to scroll until the coordinate space of a System.Windows.Media.Visual object is visible. + /// This is optimized for horizontal scrolling only + /// + /// A System.Windows.Media.Visual that becomes visible. + /// A bounding rectangle that identifies the coordinate space to make visible. + /// A System.Windows.Rect that is visible. + public Rect MakeVisible(Visual visual, Rect rectangle) + { + // We can only work on visuals that are us or children. + // An empty rect has no size or position. We can't meaningfully use it. + if (rectangle.IsEmpty + || visual == null + || visual == (Visual)this + || !this.IsAncestorOf(visual)) + { + return Rect.Empty; + } + + // Compute the child's rect relative to (0,0) in our coordinate space. + GeneralTransform childTransform = visual.TransformToAncestor(this); + + rectangle = childTransform.TransformBounds(rectangle); + + // Initialize the viewport + Rect viewport = new Rect(this.HorizontalOffset, rectangle.Top, this.ViewportWidth, rectangle.Height); + rectangle.X += viewport.X; + + // Compute the offsets required to minimally scroll the child maximally into view. + double minX = ComputeScrollOffsetWithMinimalScroll(viewport.Left, viewport.Right, rectangle.Left, rectangle.Right); + + // We have computed the scrolling offsets; scroll to them. + this.SetHorizontalOffset(minX); + + // Compute the visible rectangle of the child relative to the viewport. + viewport.X = minX; + rectangle.Intersect(viewport); + + rectangle.X -= viewport.X; + + // Return the rectangle + return rectangle; + } + + static double ComputeScrollOffsetWithMinimalScroll( + double topView, + double bottomView, + double topChild, + double bottomChild) + { + // # CHILD POSITION CHILD SIZE SCROLL REMEDY + // 1 Above viewport <= viewport Down Align top edge of child & viewport + // 2 Above viewport > viewport Down Align bottom edge of child & viewport + // 3 Below viewport <= viewport Up Align bottom edge of child & viewport + // 4 Below viewport > viewport Up Align top edge of child & viewport + // 5 Entirely within viewport NA No scroll. + // 6 Spanning viewport NA No scroll. + // + // Note: "Above viewport" = childTop above viewportTop, childBottom above viewportBottom + // "Below viewport" = childTop below viewportTop, childBottom below viewportBottom + // These child thus may overlap with the viewport, but will scroll the same direction + /*bool fAbove = DoubleUtil.LessThan(topChild, topView) && DoubleUtil.LessThan(bottomChild, bottomView); + bool fBelow = DoubleUtil.GreaterThan(bottomChild, bottomView) && DoubleUtil.GreaterThan(topChild, topView);*/ + bool fAbove = (topChild < topView) && (bottomChild < bottomView); + bool fBelow = (bottomChild > bottomView) && (topChild > topView); + bool fLarger = (bottomChild - topChild) > (bottomView - topView); + + // Handle Cases: 1 & 4 above + if ((fAbove && !fLarger) + || (fBelow && fLarger)) + { + return topChild; + } + + // Handle Cases: 2 & 3 above + else if (fAbove || fBelow) + { + return bottomChild - (bottomView - topView); + } + + // Handle cases: 5 & 6 above. + return topView; + } + + /// + /// Not implemented + /// + public void MouseWheelDown() + { + } + /// + /// Not implemented + /// + public void MouseWheelLeft() + { + } + /// + /// Not implemented + /// + public void MouseWheelRight() + { + } + /// + /// Not implemented + /// + public void MouseWheelUp() + { + } + /// + /// Not implemented + /// + public void LineDown() + { + } + /// + /// Not implemented + /// + public void LineUp() + { + } + /// + /// Not implemented + /// + public void PageDown() + { + } + /// + /// Not implemented + /// + public void PageLeft() + { + } + /// + /// Not implemented + /// + public void PageRight() + { + } + /// + /// Not implemented + /// + public void PageUp() + { + } + /// + /// Not implemented + /// + /// + public void SetVerticalOffset(double offset) + { + } + /// + /// Gets or sets a value that indicates whether scrolling on the vertical axis is possible. + /// + public bool CanVerticallyScroll + { + get { return false; } + set { } + } + /// + /// Gets or sets a value that indicates whether scrolling on the horizontal axis is possible. + /// + public bool CanHorizontallyScroll + { + get { return true; } + set { } + } + /// + /// Not implemented + /// + public double ExtentHeight + { + get { return 0.0; } + }/// + /// Not implemented + /// + + public double VerticalOffset + { + get { return 0.0; } + } + /// + /// Not implemented + /// + public double ViewportHeight + { + get { return 0.0; } + } + + // Gets scroll data info + private ScrollData ScrollData + { + get + { + return this.scrollData ?? (this.scrollData = new ScrollData()); + } + } + + // Scroll data info + private ScrollData scrollData; + private const double MinimumRegularTabWidth = 30D; + + // Validates input offset + static double ValidateInputOffset(double offset, string parameterName) + { + if (double.IsNaN(offset)) + { + throw new ArgumentOutOfRangeException(parameterName); + } + + return Math.Max(0.0, offset); + } + + // Verifies scrolling data using the passed viewport and extent as newly computed values. + // Checks the X/Y offset and coerces them into the range [0, Extent - ViewportSize] + // If extent, viewport, or the newly coerced offsets are different than the existing offset, + // cachces are updated and InvalidateScrollInfo() is called. + private void VerifyScrollData(double viewportWidth, double extentWidth) + { + bool isValid = true; + + if (double.IsInfinity(viewportWidth)) + { + viewportWidth = extentWidth; + } + + double offsetX = CoerceOffset(this.ScrollData.OffsetX, extentWidth, viewportWidth); + + isValid &= DoubleUtil.AreClose(viewportWidth, this.ScrollData.ViewportWidth); + isValid &= DoubleUtil.AreClose(extentWidth, this.ScrollData.ExtentWidth); + isValid &= DoubleUtil.AreClose(this.ScrollData.OffsetX, offsetX); + + this.ScrollData.ViewportWidth = viewportWidth; + + // newExtentWidth is neccessary to fix 20762 (Tab scroll button appears randomly when resizing) + // To fix 20762 we are manipulating the extentWidth by checking if all regular (non contextual) tabs are at their minimum width. + // When they are all at their minimum width we have to force the extentWidth to be greater than the viewportWidth. + // When there are no regular tabs, we MUST NOT apply this fix + var newExtentWidth = Math.Max(viewportWidth, extentWidth); + + var visibleRegularTabs = this.InternalChildren.Cast() + .Where(item => item.IsContextual == false && item.Visibility != Visibility.Collapsed) + .ToArray(); + + if (visibleRegularTabs.Any() + && visibleRegularTabs.All(item => DoubleUtil.AreClose(item.DesiredSize.Width, MinimumRegularTabWidth))) + { + if (DoubleUtil.AreClose(newExtentWidth, viewportWidth)) + { + newExtentWidth = newExtentWidth + 1; + } + + this.ScrollData.ExtentWidth = newExtentWidth; + } + else + { + this.ScrollData.ExtentWidth = this.ScrollData.ViewportWidth; + } + + this.ScrollData.OffsetX = offsetX; + + if (!isValid) + { + if (this.ScrollOwner != null) + { + this.ScrollOwner.InvalidateScrollInfo(); + } + } + } + + // Returns an offset coerced into the [0, Extent - Viewport] range. + static double CoerceOffset(double offset, double extent, double viewport) + { + if (offset > extent - viewport) + { + offset = extent - viewport; + } + + if (offset < 0) + { + offset = 0; + } + + return offset; + } + + #endregion + } + + #region ScrollData + + /// + /// Helper class to hold scrolling data. + /// This class exists to reduce working set when SCP is delegating to another implementation of ISI. + /// Standard "extra pointer always for less data sometimes" cache savings model: + /// + internal class ScrollData + { + /// + /// Scroll viewer + /// + internal ScrollViewer ScrollOwner; + + /// + /// Scroll offset + /// + internal double OffsetX; + + /// + /// ViewportSize is computed from our FinalSize, but may be in different units. + /// + internal double ViewportWidth; + + /// + /// Extent is the total size of our content. + /// + internal double ExtentWidth; + } + + #endregion ScrollData } \ No newline at end of file diff --git a/Fluent/Controls/RibbonTitleBar.cs b/Fluent.Ribbon/Controls/RibbonTitleBar.cs similarity index 97% rename from Fluent/Controls/RibbonTitleBar.cs rename to Fluent.Ribbon/Controls/RibbonTitleBar.cs index bc9d74b79..fbf38a823 100644 --- a/Fluent/Controls/RibbonTitleBar.cs +++ b/Fluent.Ribbon/Controls/RibbonTitleBar.cs @@ -1,417 +1,417 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Windows; -using System.Windows.Controls; -using Fluent.Internal; - -namespace Fluent -{ - /// - /// Represents title bar - /// - [StyleTypedProperty(Property = "ItemContainerStyle", StyleTargetType = typeof(RibbonContextualTabGroup))] - [TemplatePart(Name = "PART_QuickAccessToolbarHolder", Type = typeof(FrameworkElement))] - [TemplatePart(Name = "PART_HeaderHolder", Type = typeof(FrameworkElement))] - [TemplatePart(Name = "PART_ItemsContainer", Type = typeof(Panel))] - public class RibbonTitleBar : HeaderedItemsControl - { - #region Fields - - // Quick access toolbar holder - private FrameworkElement quickAccessToolbarHolder; - // Header holder - private FrameworkElement headerHolder; - // Items container - private Panel itemsContainer; - // Quick access toolbar rect - private Rect quickAccessToolbarRect; - // Header rect - private Rect headerRect; - // Items rect - private Rect itemsRect; - - #endregion - - #region Properties - - /// - /// Gets or sets quick access toolbar - /// - public UIElement QuickAccessToolBar - { - get { return (UIElement)this.GetValue(QuickAccessToolBarProperty); } - set { this.SetValue(QuickAccessToolBarProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for QuickAccessToolBar. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty QuickAccessToolBarProperty = - DependencyProperty.Register("QuickAccessToolBar", typeof(UIElement), typeof(RibbonTitleBar), new UIPropertyMetadata(null, OnQuickAccessToolbarChanged)); - - // Handles QuickAccessToolBar property chages - private static void OnQuickAccessToolbarChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var titleBar = (RibbonTitleBar)d; - titleBar.InvalidateMeasure(); - } - - /// - /// Gets or sets header alignment - /// - public HorizontalAlignment HeaderAlignment - { - get { return (HorizontalAlignment)this.GetValue(HeaderAlignmentProperty); } - set { this.SetValue(HeaderAlignmentProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for HeaderAlignment. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty HeaderAlignmentProperty = - DependencyProperty.Register("HeaderAlignment", typeof(HorizontalAlignment), typeof(RibbonTitleBar), new UIPropertyMetadata(HorizontalAlignment.Center)); - - /// - /// Defines whether title bar is collapsed - /// - public bool IsCollapsed - { - get { return (bool)this.GetValue(IsCollapsedProperty); } - set { this.SetValue(IsCollapsedProperty, value); } - } - - /// - /// DependencyProperty for - /// - public static readonly DependencyProperty IsCollapsedProperty = - DependencyProperty.Register("IsCollapsed", typeof(bool), typeof(RibbonTitleBar), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure)); - - private bool isAtLeastOneRequiredControlPresent; - - /// - /// Using a DependencyProperty as the backing store for HideContextTabs. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty HideContextTabsProperty = - DependencyProperty.Register("HideContextTabs", typeof(bool), typeof(RibbonTitleBar), new PropertyMetadata(false)); - - /// - /// Gets or sets whether context tabs are hidden. - /// - public bool HideContextTabs - { - get { return (bool)this.GetValue(HideContextTabsProperty); } - set { this.SetValue(HideContextTabsProperty, value); } - } - - #endregion - - #region Initialize - - /// - /// Static constructor - /// - [SuppressMessage("Microsoft.Performance", "CA1810")] - static RibbonTitleBar() - { - DefaultStyleKeyProperty.OverrideMetadata(typeof(RibbonTitleBar), new FrameworkPropertyMetadata(typeof(RibbonTitleBar))); - } - - #endregion - - #region Overrides - - /// - /// Creates or identifies the element that is used to display the given item. - /// - /// The element that is used to display the given item. - protected override DependencyObject GetContainerForItemOverride() - { - return new RibbonContextualTabGroup(); - } - - /// - /// Determines if the specified item is (or is eligible to be) its own container. - /// - /// The item to check. - /// true if the item is (or is eligible to be) its own container; otherwise, false. - protected override bool IsItemItsOwnContainerOverride(object item) - { - return item is RibbonContextualTabGroup; - } - - /// - /// When overridden in a derived class, is invoked whenever application code or internal processes - /// call System.Windows.FrameworkElement.ApplyTemplate(). - /// - public override void OnApplyTemplate() - { - this.quickAccessToolbarHolder = this.GetTemplateChild("PART_QuickAccessToolbarHolder") as FrameworkElement; - this.headerHolder = this.GetTemplateChild("PART_HeaderHolder") as FrameworkElement; - this.itemsContainer = this.GetTemplateChild("PART_ItemsContainer") as Panel; - - this.isAtLeastOneRequiredControlPresent = this.quickAccessToolbarHolder != null - || this.headerHolder != null - || this.itemsContainer != null; - } - - /// - /// Called to remeasure a control. - /// - /// The maximum size that the method can return. - /// The size of the control, up to the maximum specified by constraint. - protected override Size MeasureOverride(Size constraint) - { - if (this.isAtLeastOneRequiredControlPresent == false) - { - return base.MeasureOverride(constraint); - } - - if (this.IsCollapsed) - { - return base.MeasureOverride(constraint); - } - - var resultSize = constraint; - - if (double.IsPositiveInfinity(resultSize.Width) - || double.IsPositiveInfinity(resultSize.Height)) - { - resultSize = base.MeasureOverride(resultSize); - } - - this.Update(resultSize); - - this.itemsContainer.Measure(this.itemsRect.Size); - this.headerHolder.Measure(this.headerRect.Size); - this.quickAccessToolbarHolder.Measure(this.quickAccessToolbarRect.Size); - - var maxHeight = Math.Max(Math.Max(this.itemsRect.Height, this.headerRect.Height), this.quickAccessToolbarRect.Height); - var width = this.itemsRect.Width + this.headerRect.Width + this.quickAccessToolbarRect.Width; - - return new Size(width, maxHeight); - } - - /// - /// Called to arrange and size the content of a System.Windows.Controls.Control object. - /// - /// The computed size that is used to arrange the content. - /// The size of the control. - protected override Size ArrangeOverride(Size arrangeBounds) - { - if (this.isAtLeastOneRequiredControlPresent == false) - { - return base.ArrangeOverride(arrangeBounds); - } - - if (this.IsCollapsed) - { - return base.ArrangeOverride(arrangeBounds); - } - - this.itemsContainer.Arrange(this.itemsRect); - this.headerHolder.Arrange(this.headerRect); - this.quickAccessToolbarHolder.Arrange(this.quickAccessToolbarRect); - return arrangeBounds; - } - - #endregion - - #region Private methods - - // Update items size and positions - private void Update(Size constraint) - { - var visibleGroups = this.Items.OfType() - .Where(group => group.InnerVisibility == Visibility.Visible && group.Items.Count > 0) - .ToList(); - - var infinity = new Size(double.PositiveInfinity, double.PositiveInfinity); - - var canRibbonTabControlScroll = false; - - // Defensively try to find out if the RibbonTabControl can scroll - if (visibleGroups.Count > 0) - { - var firstVisibleItem = visibleGroups.First().FirstVisibleItem; - - if (firstVisibleItem != null - && firstVisibleItem.Parent != null) - { - canRibbonTabControlScroll = ((RibbonTabControl)firstVisibleItem.Parent).CanScroll; - } - } - - if (visibleGroups.Count == 0 - || canRibbonTabControlScroll) - { - // Collapse itemRect - this.itemsRect = new Rect(0, 0, 0, 0); - // Set quick launch toolbar and header position and size - this.quickAccessToolbarHolder.Measure(infinity); - - if (constraint.Width <= this.quickAccessToolbarHolder.DesiredSize.Width + 50) - { - this.quickAccessToolbarRect = new Rect(0, 0, Math.Max(0, constraint.Width - 50), this.quickAccessToolbarHolder.DesiredSize.Height); - this.quickAccessToolbarHolder.Measure(this.quickAccessToolbarRect.Size); - } - - if (constraint.Width > this.quickAccessToolbarHolder.DesiredSize.Width + 50) - { - this.quickAccessToolbarRect = new Rect(0, 0, this.quickAccessToolbarHolder.DesiredSize.Width, this.quickAccessToolbarHolder.DesiredSize.Height); - this.headerHolder.Measure(infinity); - var allTextWidth = constraint.Width - this.quickAccessToolbarHolder.DesiredSize.Width; - - if (this.HeaderAlignment == HorizontalAlignment.Left) - { - this.headerRect = new Rect(this.quickAccessToolbarHolder.DesiredSize.Width, 0, Math.Min(allTextWidth, this.headerHolder.DesiredSize.Width), constraint.Height); - } - else if (this.HeaderAlignment == HorizontalAlignment.Center) - { - this.headerRect = new Rect(this.quickAccessToolbarHolder.DesiredSize.Width + Math.Max(0, allTextWidth / 2 - this.headerHolder.DesiredSize.Width / 2), 0, Math.Min(allTextWidth, this.headerHolder.DesiredSize.Width), constraint.Height); - } - else if (this.HeaderAlignment == HorizontalAlignment.Right) - { - this.headerRect = new Rect(this.quickAccessToolbarHolder.DesiredSize.Width + Math.Max(0, allTextWidth - this.headerHolder.DesiredSize.Width), 0, Math.Min(allTextWidth, this.headerHolder.DesiredSize.Width), constraint.Height); - } - else if (this.HeaderAlignment == HorizontalAlignment.Stretch) - { - this.headerRect = new Rect(this.quickAccessToolbarHolder.DesiredSize.Width, 0, allTextWidth, constraint.Height); - } - } - else - { - this.headerRect = new Rect(Math.Max(0, constraint.Width - 50), 0, 50, constraint.Height); - } - } - else - { - // Set items container size and position - var firstItem = visibleGroups.First().FirstVisibleItem; - var lastItem = visibleGroups.Last().LastVisibleItem; - - var startX = firstItem.TranslatePoint(new Point(0, 0), this).X; - var endX = lastItem.TranslatePoint(new Point(lastItem.DesiredSize.Width, 0), this).X; - - //Get minimum x point (workaround) - foreach (var group in visibleGroups) - { - firstItem = group.FirstVisibleItem; - - if (firstItem != null) - { - if (firstItem.TranslatePoint(new Point(0, 0), this).X < startX) - { - startX = firstItem.TranslatePoint(new Point(0, 0), this).X; - } - } - - lastItem = group.LastVisibleItem; - - if (lastItem != null) - { - if (lastItem.TranslatePoint(new Point(lastItem.DesiredSize.Width, 0), this).X > endX) - { - endX = lastItem.TranslatePoint(new Point(lastItem.DesiredSize.Width, 0), this).X; - } - } - } - - // Ensure that startX and endX are never negative - startX = Math.Max(0, startX); - endX = Math.Max(0, endX); - - //Looks like thr titlebar things are ordered in an other way - if (DoubleUtil.AreClose(startX, endX) == false) - { - this.itemsRect = new Rect(startX, 0, Math.Max(0, Math.Min(endX, constraint.Width) - startX), constraint.Height); - } - - // Set quick launch toolbar position and size - this.quickAccessToolbarHolder.Measure(infinity); - - var quickAccessToolbarWidth = this.quickAccessToolbarHolder.DesiredSize.Width; - this.quickAccessToolbarRect = new Rect(0, 0, Math.Min(quickAccessToolbarWidth, startX), this.quickAccessToolbarHolder.DesiredSize.Height); - - if (quickAccessToolbarWidth > startX) - { - this.quickAccessToolbarHolder.Measure(this.quickAccessToolbarRect.Size); - this.quickAccessToolbarRect = new Rect(0, 0, this.quickAccessToolbarHolder.DesiredSize.Width, this.quickAccessToolbarHolder.DesiredSize.Height); - quickAccessToolbarWidth = this.quickAccessToolbarHolder.DesiredSize.Width; - } - - // Set header - this.headerHolder.Measure(infinity); - - switch (this.HeaderAlignment) - { - case HorizontalAlignment.Left: - { - if (startX - quickAccessToolbarWidth > 150) - { - var allTextWidth = startX - quickAccessToolbarWidth; - this.headerRect = new Rect(this.quickAccessToolbarRect.Width, 0, Math.Min(allTextWidth, this.headerHolder.DesiredSize.Width), constraint.Height); - } - else - { - var allTextWidth = Math.Max(0, constraint.Width - endX); - this.headerRect = new Rect(Math.Min(endX, constraint.Width), 0, Math.Min(allTextWidth, this.headerHolder.DesiredSize.Width), constraint.Height); - } - } - break; - - case HorizontalAlignment.Center: - { - var allTextWidthRight = Math.Max(0, constraint.Width - endX); - var allTextWidthLeft = Math.Max(0, startX - quickAccessToolbarWidth); - var fitsRightButNotLeft = (allTextWidthRight >= this.headerHolder.DesiredSize.Width && allTextWidthLeft < this.headerHolder.DesiredSize.Width); - - if (((startX - quickAccessToolbarWidth < 150 || fitsRightButNotLeft) && (startX - quickAccessToolbarWidth > 0) && (startX - quickAccessToolbarWidth < constraint.Width - endX)) || (endX < constraint.Width / 2)) - { - this.headerRect = new Rect(Math.Min(Math.Max(endX, constraint.Width / 2 - this.headerHolder.DesiredSize.Width / 2), constraint.Width), 0, Math.Min(allTextWidthRight, this.headerHolder.DesiredSize.Width), constraint.Height); - } - else - { - this.headerRect = new Rect(this.quickAccessToolbarHolder.DesiredSize.Width + Math.Max(0, allTextWidthLeft / 2 - this.headerHolder.DesiredSize.Width / 2), 0, Math.Min(allTextWidthLeft, this.headerHolder.DesiredSize.Width), constraint.Height); - } - } - break; - - case HorizontalAlignment.Right: - { - if (startX - quickAccessToolbarWidth > 150) - { - var allTextWidth = Math.Max(0, startX - quickAccessToolbarWidth); - this.headerRect = new Rect(this.quickAccessToolbarHolder.DesiredSize.Width + Math.Max(0, allTextWidth - this.headerHolder.DesiredSize.Width), 0, Math.Min(allTextWidth, this.headerHolder.DesiredSize.Width), constraint.Height); - } - else - { - var allTextWidth = Math.Max(0, constraint.Width - endX); - this.headerRect = new Rect(Math.Min(Math.Max(endX, constraint.Width - this.headerHolder.DesiredSize.Width), constraint.Width), 0, Math.Min(allTextWidth, this.headerHolder.DesiredSize.Width), constraint.Height); - } - } - break; - - case HorizontalAlignment.Stretch: - { - if (startX - quickAccessToolbarWidth > 150) - { - var allTextWidth = startX - quickAccessToolbarWidth; - this.headerRect = new Rect(this.quickAccessToolbarRect.Width, 0, allTextWidth, constraint.Height); - } - else - { - var allTextWidth = Math.Max(0, constraint.Width - endX); - this.headerRect = new Rect(Math.Min(endX, constraint.Width), 0, allTextWidth, constraint.Height); - } - } - break; - } - } - - this.headerRect.Width = this.headerRect.Width + 2; - } - - #endregion - } -} +using System; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Windows; +using System.Windows.Controls; +using Fluent.Internal; + +namespace Fluent +{ + /// + /// Represents title bar + /// + [StyleTypedProperty(Property = "ItemContainerStyle", StyleTargetType = typeof(RibbonContextualTabGroup))] + [TemplatePart(Name = "PART_QuickAccessToolbarHolder", Type = typeof(FrameworkElement))] + [TemplatePart(Name = "PART_HeaderHolder", Type = typeof(FrameworkElement))] + [TemplatePart(Name = "PART_ItemsContainer", Type = typeof(Panel))] + public class RibbonTitleBar : HeaderedItemsControl + { + #region Fields + + // Quick access toolbar holder + private FrameworkElement quickAccessToolbarHolder; + // Header holder + private FrameworkElement headerHolder; + // Items container + private Panel itemsContainer; + // Quick access toolbar rect + private Rect quickAccessToolbarRect; + // Header rect + private Rect headerRect; + // Items rect + private Rect itemsRect; + + #endregion + + #region Properties + + /// + /// Gets or sets quick access toolbar + /// + public UIElement QuickAccessToolBar + { + get { return (UIElement)this.GetValue(QuickAccessToolBarProperty); } + set { this.SetValue(QuickAccessToolBarProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for QuickAccessToolBar. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty QuickAccessToolBarProperty = + DependencyProperty.Register("QuickAccessToolBar", typeof(UIElement), typeof(RibbonTitleBar), new UIPropertyMetadata(null, OnQuickAccessToolbarChanged)); + + // Handles QuickAccessToolBar property chages + private static void OnQuickAccessToolbarChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var titleBar = (RibbonTitleBar)d; + titleBar.InvalidateMeasure(); + } + + /// + /// Gets or sets header alignment + /// + public HorizontalAlignment HeaderAlignment + { + get { return (HorizontalAlignment)this.GetValue(HeaderAlignmentProperty); } + set { this.SetValue(HeaderAlignmentProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for HeaderAlignment. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty HeaderAlignmentProperty = + DependencyProperty.Register("HeaderAlignment", typeof(HorizontalAlignment), typeof(RibbonTitleBar), new UIPropertyMetadata(HorizontalAlignment.Center)); + + /// + /// Defines whether title bar is collapsed + /// + public bool IsCollapsed + { + get { return (bool)this.GetValue(IsCollapsedProperty); } + set { this.SetValue(IsCollapsedProperty, value); } + } + + /// + /// DependencyProperty for + /// + public static readonly DependencyProperty IsCollapsedProperty = + DependencyProperty.Register("IsCollapsed", typeof(bool), typeof(RibbonTitleBar), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure)); + + private bool isAtLeastOneRequiredControlPresent; + + /// + /// Using a DependencyProperty as the backing store for HideContextTabs. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty HideContextTabsProperty = + DependencyProperty.Register("HideContextTabs", typeof(bool), typeof(RibbonTitleBar), new PropertyMetadata(false)); + + /// + /// Gets or sets whether context tabs are hidden. + /// + public bool HideContextTabs + { + get { return (bool)this.GetValue(HideContextTabsProperty); } + set { this.SetValue(HideContextTabsProperty, value); } + } + + #endregion + + #region Initialize + + /// + /// Static constructor + /// + [SuppressMessage("Microsoft.Performance", "CA1810")] + static RibbonTitleBar() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(RibbonTitleBar), new FrameworkPropertyMetadata(typeof(RibbonTitleBar))); + } + + #endregion + + #region Overrides + + /// + /// Creates or identifies the element that is used to display the given item. + /// + /// The element that is used to display the given item. + protected override DependencyObject GetContainerForItemOverride() + { + return new RibbonContextualTabGroup(); + } + + /// + /// Determines if the specified item is (or is eligible to be) its own container. + /// + /// The item to check. + /// true if the item is (or is eligible to be) its own container; otherwise, false. + protected override bool IsItemItsOwnContainerOverride(object item) + { + return item is RibbonContextualTabGroup; + } + + /// + /// When overridden in a derived class, is invoked whenever application code or internal processes + /// call System.Windows.FrameworkElement.ApplyTemplate(). + /// + public override void OnApplyTemplate() + { + this.quickAccessToolbarHolder = this.GetTemplateChild("PART_QuickAccessToolbarHolder") as FrameworkElement; + this.headerHolder = this.GetTemplateChild("PART_HeaderHolder") as FrameworkElement; + this.itemsContainer = this.GetTemplateChild("PART_ItemsContainer") as Panel; + + this.isAtLeastOneRequiredControlPresent = this.quickAccessToolbarHolder != null + || this.headerHolder != null + || this.itemsContainer != null; + } + + /// + /// Called to remeasure a control. + /// + /// The maximum size that the method can return. + /// The size of the control, up to the maximum specified by constraint. + protected override Size MeasureOverride(Size constraint) + { + if (this.isAtLeastOneRequiredControlPresent == false) + { + return base.MeasureOverride(constraint); + } + + if (this.IsCollapsed) + { + return base.MeasureOverride(constraint); + } + + var resultSize = constraint; + + if (double.IsPositiveInfinity(resultSize.Width) + || double.IsPositiveInfinity(resultSize.Height)) + { + resultSize = base.MeasureOverride(resultSize); + } + + this.Update(resultSize); + + this.itemsContainer.Measure(this.itemsRect.Size); + this.headerHolder.Measure(this.headerRect.Size); + this.quickAccessToolbarHolder.Measure(this.quickAccessToolbarRect.Size); + + var maxHeight = Math.Max(Math.Max(this.itemsRect.Height, this.headerRect.Height), this.quickAccessToolbarRect.Height); + var width = this.itemsRect.Width + this.headerRect.Width + this.quickAccessToolbarRect.Width; + + return new Size(width, maxHeight); + } + + /// + /// Called to arrange and size the content of a System.Windows.Controls.Control object. + /// + /// The computed size that is used to arrange the content. + /// The size of the control. + protected override Size ArrangeOverride(Size arrangeBounds) + { + if (this.isAtLeastOneRequiredControlPresent == false) + { + return base.ArrangeOverride(arrangeBounds); + } + + if (this.IsCollapsed) + { + return base.ArrangeOverride(arrangeBounds); + } + + this.itemsContainer.Arrange(this.itemsRect); + this.headerHolder.Arrange(this.headerRect); + this.quickAccessToolbarHolder.Arrange(this.quickAccessToolbarRect); + return arrangeBounds; + } + + #endregion + + #region Private methods + + // Update items size and positions + private void Update(Size constraint) + { + var visibleGroups = this.Items.OfType() + .Where(group => group.InnerVisibility == Visibility.Visible && group.Items.Count > 0) + .ToList(); + + var infinity = new Size(double.PositiveInfinity, double.PositiveInfinity); + + var canRibbonTabControlScroll = false; + + // Defensively try to find out if the RibbonTabControl can scroll + if (visibleGroups.Count > 0) + { + var firstVisibleItem = visibleGroups.First().FirstVisibleItem; + + if (firstVisibleItem != null + && firstVisibleItem.Parent != null) + { + canRibbonTabControlScroll = ((RibbonTabControl)firstVisibleItem.Parent).CanScroll; + } + } + + if (visibleGroups.Count == 0 + || canRibbonTabControlScroll) + { + // Collapse itemRect + this.itemsRect = new Rect(0, 0, 0, 0); + // Set quick launch toolbar and header position and size + this.quickAccessToolbarHolder.Measure(infinity); + + if (constraint.Width <= this.quickAccessToolbarHolder.DesiredSize.Width + 50) + { + this.quickAccessToolbarRect = new Rect(0, 0, Math.Max(0, constraint.Width - 50), this.quickAccessToolbarHolder.DesiredSize.Height); + this.quickAccessToolbarHolder.Measure(this.quickAccessToolbarRect.Size); + } + + if (constraint.Width > this.quickAccessToolbarHolder.DesiredSize.Width + 50) + { + this.quickAccessToolbarRect = new Rect(0, 0, this.quickAccessToolbarHolder.DesiredSize.Width, this.quickAccessToolbarHolder.DesiredSize.Height); + this.headerHolder.Measure(infinity); + var allTextWidth = constraint.Width - this.quickAccessToolbarHolder.DesiredSize.Width; + + if (this.HeaderAlignment == HorizontalAlignment.Left) + { + this.headerRect = new Rect(this.quickAccessToolbarHolder.DesiredSize.Width, 0, Math.Min(allTextWidth, this.headerHolder.DesiredSize.Width), constraint.Height); + } + else if (this.HeaderAlignment == HorizontalAlignment.Center) + { + this.headerRect = new Rect(this.quickAccessToolbarHolder.DesiredSize.Width + Math.Max(0, allTextWidth / 2 - this.headerHolder.DesiredSize.Width / 2), 0, Math.Min(allTextWidth, this.headerHolder.DesiredSize.Width), constraint.Height); + } + else if (this.HeaderAlignment == HorizontalAlignment.Right) + { + this.headerRect = new Rect(this.quickAccessToolbarHolder.DesiredSize.Width + Math.Max(0, allTextWidth - this.headerHolder.DesiredSize.Width), 0, Math.Min(allTextWidth, this.headerHolder.DesiredSize.Width), constraint.Height); + } + else if (this.HeaderAlignment == HorizontalAlignment.Stretch) + { + this.headerRect = new Rect(this.quickAccessToolbarHolder.DesiredSize.Width, 0, allTextWidth, constraint.Height); + } + } + else + { + this.headerRect = new Rect(Math.Max(0, constraint.Width - 50), 0, 50, constraint.Height); + } + } + else + { + // Set items container size and position + var firstItem = visibleGroups.First().FirstVisibleItem; + var lastItem = visibleGroups.Last().LastVisibleItem; + + var startX = firstItem.TranslatePoint(new Point(0, 0), this).X; + var endX = lastItem.TranslatePoint(new Point(lastItem.DesiredSize.Width, 0), this).X; + + //Get minimum x point (workaround) + foreach (var group in visibleGroups) + { + firstItem = group.FirstVisibleItem; + + if (firstItem != null) + { + if (firstItem.TranslatePoint(new Point(0, 0), this).X < startX) + { + startX = firstItem.TranslatePoint(new Point(0, 0), this).X; + } + } + + lastItem = group.LastVisibleItem; + + if (lastItem != null) + { + if (lastItem.TranslatePoint(new Point(lastItem.DesiredSize.Width, 0), this).X > endX) + { + endX = lastItem.TranslatePoint(new Point(lastItem.DesiredSize.Width, 0), this).X; + } + } + } + + // Ensure that startX and endX are never negative + startX = Math.Max(0, startX); + endX = Math.Max(0, endX); + + //Looks like thr titlebar things are ordered in an other way + if (DoubleUtil.AreClose(startX, endX) == false) + { + this.itemsRect = new Rect(startX, 0, Math.Max(0, Math.Min(endX, constraint.Width) - startX), constraint.Height); + } + + // Set quick launch toolbar position and size + this.quickAccessToolbarHolder.Measure(infinity); + + var quickAccessToolbarWidth = this.quickAccessToolbarHolder.DesiredSize.Width; + this.quickAccessToolbarRect = new Rect(0, 0, Math.Min(quickAccessToolbarWidth, startX), this.quickAccessToolbarHolder.DesiredSize.Height); + + if (quickAccessToolbarWidth > startX) + { + this.quickAccessToolbarHolder.Measure(this.quickAccessToolbarRect.Size); + this.quickAccessToolbarRect = new Rect(0, 0, this.quickAccessToolbarHolder.DesiredSize.Width, this.quickAccessToolbarHolder.DesiredSize.Height); + quickAccessToolbarWidth = this.quickAccessToolbarHolder.DesiredSize.Width; + } + + // Set header + this.headerHolder.Measure(infinity); + + switch (this.HeaderAlignment) + { + case HorizontalAlignment.Left: + { + if (startX - quickAccessToolbarWidth > 150) + { + var allTextWidth = startX - quickAccessToolbarWidth; + this.headerRect = new Rect(this.quickAccessToolbarRect.Width, 0, Math.Min(allTextWidth, this.headerHolder.DesiredSize.Width), constraint.Height); + } + else + { + var allTextWidth = Math.Max(0, constraint.Width - endX); + this.headerRect = new Rect(Math.Min(endX, constraint.Width), 0, Math.Min(allTextWidth, this.headerHolder.DesiredSize.Width), constraint.Height); + } + } + break; + + case HorizontalAlignment.Center: + { + var allTextWidthRight = Math.Max(0, constraint.Width - endX); + var allTextWidthLeft = Math.Max(0, startX - quickAccessToolbarWidth); + var fitsRightButNotLeft = (allTextWidthRight >= this.headerHolder.DesiredSize.Width && allTextWidthLeft < this.headerHolder.DesiredSize.Width); + + if (((startX - quickAccessToolbarWidth < 150 || fitsRightButNotLeft) && (startX - quickAccessToolbarWidth > 0) && (startX - quickAccessToolbarWidth < constraint.Width - endX)) || (endX < constraint.Width / 2)) + { + this.headerRect = new Rect(Math.Min(Math.Max(endX, constraint.Width / 2 - this.headerHolder.DesiredSize.Width / 2), constraint.Width), 0, Math.Min(allTextWidthRight, this.headerHolder.DesiredSize.Width), constraint.Height); + } + else + { + this.headerRect = new Rect(this.quickAccessToolbarHolder.DesiredSize.Width + Math.Max(0, allTextWidthLeft / 2 - this.headerHolder.DesiredSize.Width / 2), 0, Math.Min(allTextWidthLeft, this.headerHolder.DesiredSize.Width), constraint.Height); + } + } + break; + + case HorizontalAlignment.Right: + { + if (startX - quickAccessToolbarWidth > 150) + { + var allTextWidth = Math.Max(0, startX - quickAccessToolbarWidth); + this.headerRect = new Rect(this.quickAccessToolbarHolder.DesiredSize.Width + Math.Max(0, allTextWidth - this.headerHolder.DesiredSize.Width), 0, Math.Min(allTextWidth, this.headerHolder.DesiredSize.Width), constraint.Height); + } + else + { + var allTextWidth = Math.Max(0, constraint.Width - endX); + this.headerRect = new Rect(Math.Min(Math.Max(endX, constraint.Width - this.headerHolder.DesiredSize.Width), constraint.Width), 0, Math.Min(allTextWidth, this.headerHolder.DesiredSize.Width), constraint.Height); + } + } + break; + + case HorizontalAlignment.Stretch: + { + if (startX - quickAccessToolbarWidth > 150) + { + var allTextWidth = startX - quickAccessToolbarWidth; + this.headerRect = new Rect(this.quickAccessToolbarRect.Width, 0, allTextWidth, constraint.Height); + } + else + { + var allTextWidth = Math.Max(0, constraint.Width - endX); + this.headerRect = new Rect(Math.Min(endX, constraint.Width), 0, allTextWidth, constraint.Height); + } + } + break; + } + } + + this.headerRect.Width = this.headerRect.Width + 2; + } + + #endregion + } +} diff --git a/Fluent/Controls/RibbonToolBar.cs b/Fluent.Ribbon/Controls/RibbonToolBar.cs similarity index 97% rename from Fluent/Controls/RibbonToolBar.cs rename to Fluent.Ribbon/Controls/RibbonToolBar.cs index e966b91d3..531dc6df0 100644 --- a/Fluent/Controls/RibbonToolBar.cs +++ b/Fluent.Ribbon/Controls/RibbonToolBar.cs @@ -1,582 +1,582 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Collections; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Collections.Specialized; -using System.ComponentModel; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Markup; -using System.Windows.Media; - -namespace Fluent -{ - using Fluent.Extensibility; - - /// - /// Represent panel for group box panel - /// - [ContentProperty("Children")] - public class RibbonToolBar : RibbonControl, IRibbonSizeChangedSink - { - #region Fields - - // User defined children - readonly ObservableCollection children = new ObservableCollection(); - // User defined layout definitions - readonly ObservableCollection layoutDefinitions = - new ObservableCollection(); - - // Actual children - readonly List actualChildren = new List(); - // Designates that rebuilding of visual & logical children is required - bool rebuildVisualAndLogicalChildren = true; - - #endregion - - #region Properties - - #region Separator Style - - /// - /// Gets or sets style for the separator - /// - public Style SeparatorStyle - { - get { return (Style)this.GetValue(SeparatorStyleProperty); } - set { this.SetValue(SeparatorStyleProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for SeparatorStyle. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty SeparatorStyleProperty = - DependencyProperty.Register("SeparatorStyle", typeof(Style), - typeof(RibbonToolBar), new UIPropertyMetadata(null, OnSeparatorStyleChanged)); - - static void OnSeparatorStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - RibbonToolBar toolBar = (RibbonToolBar)d; - toolBar.rebuildVisualAndLogicalChildren = true; - toolBar.InvalidateMeasure(); - } - - #endregion - - /// - /// Gets children - /// - [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] - public ObservableCollection Children - { - get { return this.children; } - } - - /// - /// Gets particular rules for layout in this group box panel - /// - public ObservableCollection LayoutDefinitions - { - get { return this.layoutDefinitions; } - } - - #endregion - - #region Logical & Visual Tree - - /// - /// Gets the number of visual child elements within this element. - /// - protected override int VisualChildrenCount - { - get - { - if (this.layoutDefinitions.Count == 0) return this.children.Count; - if (this.rebuildVisualAndLogicalChildren) - { - //TODO: Exception during theme changing - // UpdateLayout(); - this.InvalidateMeasure(); - } - return this.actualChildren.Count; - } - } - - /// - /// Overrides System.Windows.Media.Visual.GetVisualChild(System.Int32), - /// and returns a child at the specified index from a collection of child elements. - /// - /// The zero-based index of the requested - /// child element in the collection - /// The requested child element. This should not return null; - /// if the provided index is out of range, an exception is thrown - protected override Visual GetVisualChild(int index) - { - if (this.layoutDefinitions.Count == 0) return this.children[index]; - if (this.rebuildVisualAndLogicalChildren) - { - // UpdateLayout(); - this.InvalidateMeasure(); - } - return this.actualChildren[index]; - } - - /// - /// Gets an enumerator for logical child elements of this element - /// - protected override IEnumerator LogicalChildren - { - get - { - return this.children.GetEnumerator(); - } - } - - #endregion - - #region Initialization - - /// - /// Static constructor - /// - [SuppressMessage("Microsoft.Performance", "CA1810")] - static RibbonToolBar() - { - // Override default style - DefaultStyleKeyProperty.OverrideMetadata(typeof(RibbonToolBar), new FrameworkPropertyMetadata(typeof(RibbonToolBar))); - // Disable QAT for this control - CanAddToQuickAccessToolBarProperty.OverrideMetadata(typeof(RibbonToolBar), new FrameworkPropertyMetadata(false)); - StyleProperty.OverrideMetadata(typeof(RibbonToolBar), new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceStyle))); - } - - // Coerce object style - static object OnCoerceStyle(DependencyObject d, object basevalue) - { - if (basevalue == null) - { - basevalue = (d as FrameworkElement).TryFindResource(typeof(RibbonToolBar)); - } - - return basevalue; - } - - /// - /// Default constructor - /// - public RibbonToolBar() - { - this.children.CollectionChanged += this.OnChildrenCollectionChanged; - this.layoutDefinitions.CollectionChanged += this.OnLayoutDefinitionsChanged; - } - - void OnLayoutDefinitionsChanged(object sender, NotifyCollectionChangedEventArgs e) - { - this.rebuildVisualAndLogicalChildren = true; - this.InvalidateMeasure(); - } - - void OnChildrenCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) - { - // Children have changed, reset layouts - this.rebuildVisualAndLogicalChildren = true; - this.InvalidateMeasure(); - } - - #endregion - - #region Methods - - /// - /// Gets current used layout definition (or null if no present definitions) - /// - /// Layout definition or null - internal RibbonToolBarLayoutDefinition GetCurrentLayoutDefinition() - { - if (this.layoutDefinitions.Count == 0) return null; - if (this.layoutDefinitions.Count == 1) return this.layoutDefinitions[0]; - - foreach (RibbonToolBarLayoutDefinition definition in this.layoutDefinitions) - { - if (RibbonProperties.GetSize(definition) == RibbonProperties.GetSize(this)) return definition; - } - - // TODO: try to find a better definition - return this.layoutDefinitions[0]; - } - - #endregion - - #region Size Property Changing - - /// - /// Handles size property changing - /// - /// Previous value - /// Current value - public void OnSizePropertyChanged(RibbonControlSize previous, RibbonControlSize current) - { - foreach (var frameworkElement in this.actualChildren) - { - RibbonProperties.SetSize(frameworkElement, current); - } - - this.rebuildVisualAndLogicalChildren = true; - this.InvalidateMeasure(); - } - - #endregion - - #region Layout Overriding - - /// - /// Measures all of the RibbonGroupBox, and resize them appropriately - /// to fit within the available room - /// - /// The available size that - /// this element can give to child elements. - /// The size that the panel determines it needs during - /// layout, based on its calculations of child element sizes. - /// - protected override Size MeasureOverride(Size availableSize) - { - RibbonToolBarLayoutDefinition layoutDefinition = this.GetCurrentLayoutDefinition(); - - // Rebuilding actual children (visual & logical) - if (this.rebuildVisualAndLogicalChildren) - { - // Clear previous children - foreach (FrameworkElement child in this.actualChildren) - { - RibbonToolBarControlGroup controlGroup = child as RibbonToolBarControlGroup; - if (controlGroup != null) controlGroup.Items.Clear(); - this.RemoveVisualChild(child); - this.RemoveLogicalChild(child); - } - this.actualChildren.Clear(); - this.cachedControlGroups.Clear(); - } - - if (layoutDefinition == null) - { - if (this.rebuildVisualAndLogicalChildren) - { - // If default layout is used add all children - foreach (FrameworkElement child in this.Children) - { - this.actualChildren.Add(child); - this.AddVisualChild(child); - this.AddLogicalChild(child); - } - this.rebuildVisualAndLogicalChildren = false; - } - return this.WrapPanelLayuot(availableSize, true); - } - else - { - Size result = this.CustomLayout(layoutDefinition, availableSize, true, this.rebuildVisualAndLogicalChildren); - this.rebuildVisualAndLogicalChildren = false; - return result; - } - - - } - - /// - /// When overridden in a derived class, positions child elements and determines - /// a size for a System.Windows.FrameworkElement derived class. - /// - /// The final area within the parent that this - /// element should use to arrange itself and its children. - /// The actual size used. - protected override Size ArrangeOverride(Size finalSize) - { - RibbonToolBarLayoutDefinition layoutDefinition = this.GetCurrentLayoutDefinition(); - if (layoutDefinition == null) return this.WrapPanelLayuot(finalSize, false); - return this.CustomLayout(layoutDefinition, finalSize, false, false); - } - - - #region Wrap Panel Layout - - /// - /// Unified method for wrap panel logic - /// - /// Available or final size - /// Pass true if measure required; pass false if arrange required - /// Final size - Size WrapPanelLayuot(Size availableSize, bool measure) - { - bool arrange = !measure; - double availableHeight = double.IsPositiveInfinity(availableSize.Height) ? 0 : availableSize.Height; - - double currentheight = 0; - double columnWidth = 0; - - double resultWidth = 0; - double resultHeight = 0; - - Size infinity = new Size(double.PositiveInfinity, double.PositiveInfinity); - foreach (FrameworkElement child in this.children) - { - // Measuring - if (measure) child.Measure(infinity); - - if (currentheight + child.DesiredSize.Height > availableHeight) - { - // Move to the next column - resultHeight = Math.Max(resultHeight, currentheight); - resultWidth += columnWidth; - currentheight = 0; - columnWidth = 0; - } - - // Arranging - if (arrange) child.Arrange(new Rect( - resultWidth, - currentheight, - child.DesiredSize.Width, - child.DesiredSize.Height)); - - columnWidth = Math.Max(columnWidth, child.DesiredSize.Width); - currentheight += child.DesiredSize.Height; - } - - return new Size(resultWidth + columnWidth, resultHeight); - } - - #endregion - - #region Control and Group Creation from a Definition - - FrameworkElement GetControl(RibbonToolBarControlDefinition controlDefinition) - { - string name = controlDefinition.Target; - return this.Children.FirstOrDefault(x => x.Name == name); - } - - Dictionary cachedControlGroups = new Dictionary(); - RibbonToolBarControlGroup GetControlGroup(RibbonToolBarControlGroupDefinition controlGroupDefinition) - { - RibbonToolBarControlGroup controlGroup = null; - if (!this.cachedControlGroups.TryGetValue(controlGroupDefinition, out controlGroup)) - { - controlGroup = new RibbonToolBarControlGroup(); - // Add items to the group - foreach (RibbonToolBarControlDefinition child in controlGroupDefinition.Children) - { - controlGroup.Items.Add(this.GetControl(child)); - } - this.cachedControlGroups.Add(controlGroupDefinition, controlGroup); - } - return controlGroup; - } - - #endregion - - #region Custom Layout - - // Cached separators (clear & set in Measure pass) - Dictionary separatorCache = new Dictionary(); - - /// - /// Layout logic for the given layout definition - /// - /// Current layout definition - /// Available or final size - /// Pass true if measure required; pass false if arrange required - /// Determines whether we have to add children to the logical and visual tree - /// Final size - Size CustomLayout(RibbonToolBarLayoutDefinition layoutDefinition, Size availableSize, bool measure, bool addchildren) - { - bool arrange = !measure; - double availableHeight = double.IsPositiveInfinity(availableSize.Height) ? 0 : availableSize.Height; - - // Clear separator cahce - if (addchildren) - this.separatorCache.Clear(); - - // Get the first control and measure, its height accepts as row height - double rowHeight = this.GetRowHeight(layoutDefinition); - - - // Calculate whitespace - int rowCountInColumn = Math.Min(layoutDefinition.RowCount, layoutDefinition.Rows.Count); - double whitespace = (availableHeight - ((double)rowCountInColumn * rowHeight)) / (double)(rowCountInColumn + 1); - - double y = 0; - double x = 0; - double currentRowBegin = 0; - double currentMaxX = 0; - double maxy = 0; - for(int rowIndex = 0; rowIndex < layoutDefinition.Rows.Count; rowIndex++) - { - RibbonToolBarRow row = layoutDefinition.Rows[rowIndex]; - - x = currentRowBegin; - - if (rowIndex % rowCountInColumn == 0) - { - // Reset vars at new column - x = currentRowBegin = currentMaxX; - y = 0; - - if (rowIndex != 0) - { - #region Add separator - - Separator separator = null; - if (!this.separatorCache.TryGetValue(rowIndex, out separator)) - { - separator = new Separator(); - separator.Style = this.SeparatorStyle; - this.separatorCache.Add(rowIndex, separator); - } - if (measure) - { - separator.Height = availableHeight - separator.Margin.Bottom - separator.Margin.Top; - separator.Measure(availableSize); - } - if (arrange) separator.Arrange(new Rect(x, y, - separator.DesiredSize.Width, - separator.DesiredSize.Height)); - x += separator.DesiredSize.Width; - - if (addchildren) - { - // Add control in the children - this.AddVisualChild(separator); - this.AddLogicalChild(separator); - this.actualChildren.Add(separator); - } - - #endregion - } - } - y += whitespace; - - - // Measure & arrange new row - for(int i = 0; i < row.Children.Count; i++) - { - if (row.Children[i] is RibbonToolBarControlDefinition) - { - // Control Definition Case - RibbonToolBarControlDefinition controlDefinition = - (RibbonToolBarControlDefinition) row.Children[i]; - FrameworkElement control = this.GetControl(controlDefinition); - if (control == null) continue; - - if (addchildren) - { - // Add control in the children - this.AddVisualChild(control); - this.AddLogicalChild(control); - this.actualChildren.Add(control); - } - - if (measure) - { - // Apply Control Definition Properties - RibbonProperties.SetSize(control, RibbonProperties.GetSize(controlDefinition)); - control.Width = controlDefinition.Width; - control.Measure(availableSize); - } - if (arrange) - { - control.Arrange(new Rect(x, y, - control.DesiredSize.Width, - control.DesiredSize.Height)); - } - - x += control.DesiredSize.Width; - } - if (row.Children[i] is RibbonToolBarControlGroupDefinition) - { - // Control Definition Case - RibbonToolBarControlGroupDefinition controlGroupDefinition = - (RibbonToolBarControlGroupDefinition)row.Children[i]; - - RibbonToolBarControlGroup control = this.GetControlGroup(controlGroupDefinition); - - if (addchildren) - { - // Add control in the children - this.AddVisualChild(control); - this.AddLogicalChild(control); - this.actualChildren.Add(control); - } - - if (measure) - { - // Apply Control Definition Properties - control.IsFirstInRow = (i == 0); - control.IsLastInRow = (i == row.Children.Count - 1); - control.Measure(availableSize); - } - if (arrange) - { - control.Arrange(new Rect(x, y, - control.DesiredSize.Width, - control.DesiredSize.Height)); - } - - x += control.DesiredSize.Width; - } - } - - y += rowHeight; - if (currentMaxX < x) currentMaxX = x; - if (maxy < y) maxy = y; - } - - return new Size(currentMaxX, maxy + whitespace); - } - - // Get the first control and measure, its height accepts as row height - double GetRowHeight(RibbonToolBarLayoutDefinition layoutDefinition) - { - const double defaultRowHeight = 0; - foreach (RibbonToolBarRow row in layoutDefinition.Rows) - { - foreach (DependencyObject item in row.Children) - { - RibbonToolBarControlDefinition controlDefinition = item as RibbonToolBarControlDefinition; - RibbonToolBarControlGroupDefinition controlGroupDefinition = item as RibbonToolBarControlGroupDefinition; - FrameworkElement control = null; - if (controlDefinition != null) control = this.GetControl(controlDefinition); - else if (controlGroupDefinition != null) - control = this.GetControlGroup(controlGroupDefinition); - - if (control == null) return defaultRowHeight; - control.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); - return control.DesiredSize.Height; - } - } - return defaultRowHeight; - } - - #endregion - - #endregion - - #region QAT Support - - // (!) RibbonToolBar must not to be in QAT - - /// - /// Gets control which represents shortcut item. - /// This item MUST be syncronized with the original - /// and send command to original one control. - /// - /// Control which represents shortcut item - public override FrameworkElement CreateQuickAccessItem() - { - return new Control(); - } - - #endregion - } +using System; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Markup; +using System.Windows.Media; + +namespace Fluent +{ + using Fluent.Extensibility; + + /// + /// Represent panel for group box panel + /// + [ContentProperty("Children")] + public class RibbonToolBar : RibbonControl, IRibbonSizeChangedSink + { + #region Fields + + // User defined children + readonly ObservableCollection children = new ObservableCollection(); + // User defined layout definitions + readonly ObservableCollection layoutDefinitions = + new ObservableCollection(); + + // Actual children + readonly List actualChildren = new List(); + // Designates that rebuilding of visual & logical children is required + bool rebuildVisualAndLogicalChildren = true; + + #endregion + + #region Properties + + #region Separator Style + + /// + /// Gets or sets style for the separator + /// + public Style SeparatorStyle + { + get { return (Style)this.GetValue(SeparatorStyleProperty); } + set { this.SetValue(SeparatorStyleProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for SeparatorStyle. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty SeparatorStyleProperty = + DependencyProperty.Register("SeparatorStyle", typeof(Style), + typeof(RibbonToolBar), new UIPropertyMetadata(null, OnSeparatorStyleChanged)); + + static void OnSeparatorStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + RibbonToolBar toolBar = (RibbonToolBar)d; + toolBar.rebuildVisualAndLogicalChildren = true; + toolBar.InvalidateMeasure(); + } + + #endregion + + /// + /// Gets children + /// + [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] + public ObservableCollection Children + { + get { return this.children; } + } + + /// + /// Gets particular rules for layout in this group box panel + /// + public ObservableCollection LayoutDefinitions + { + get { return this.layoutDefinitions; } + } + + #endregion + + #region Logical & Visual Tree + + /// + /// Gets the number of visual child elements within this element. + /// + protected override int VisualChildrenCount + { + get + { + if (this.layoutDefinitions.Count == 0) return this.children.Count; + if (this.rebuildVisualAndLogicalChildren) + { + //TODO: Exception during theme changing + // UpdateLayout(); + this.InvalidateMeasure(); + } + return this.actualChildren.Count; + } + } + + /// + /// Overrides System.Windows.Media.Visual.GetVisualChild(System.Int32), + /// and returns a child at the specified index from a collection of child elements. + /// + /// The zero-based index of the requested + /// child element in the collection + /// The requested child element. This should not return null; + /// if the provided index is out of range, an exception is thrown + protected override Visual GetVisualChild(int index) + { + if (this.layoutDefinitions.Count == 0) return this.children[index]; + if (this.rebuildVisualAndLogicalChildren) + { + // UpdateLayout(); + this.InvalidateMeasure(); + } + return this.actualChildren[index]; + } + + /// + /// Gets an enumerator for logical child elements of this element + /// + protected override IEnumerator LogicalChildren + { + get + { + return this.children.GetEnumerator(); + } + } + + #endregion + + #region Initialization + + /// + /// Static constructor + /// + [SuppressMessage("Microsoft.Performance", "CA1810")] + static RibbonToolBar() + { + // Override default style + DefaultStyleKeyProperty.OverrideMetadata(typeof(RibbonToolBar), new FrameworkPropertyMetadata(typeof(RibbonToolBar))); + // Disable QAT for this control + CanAddToQuickAccessToolBarProperty.OverrideMetadata(typeof(RibbonToolBar), new FrameworkPropertyMetadata(false)); + StyleProperty.OverrideMetadata(typeof(RibbonToolBar), new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceStyle))); + } + + // Coerce object style + static object OnCoerceStyle(DependencyObject d, object basevalue) + { + if (basevalue == null) + { + basevalue = (d as FrameworkElement).TryFindResource(typeof(RibbonToolBar)); + } + + return basevalue; + } + + /// + /// Default constructor + /// + public RibbonToolBar() + { + this.children.CollectionChanged += this.OnChildrenCollectionChanged; + this.layoutDefinitions.CollectionChanged += this.OnLayoutDefinitionsChanged; + } + + void OnLayoutDefinitionsChanged(object sender, NotifyCollectionChangedEventArgs e) + { + this.rebuildVisualAndLogicalChildren = true; + this.InvalidateMeasure(); + } + + void OnChildrenCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + // Children have changed, reset layouts + this.rebuildVisualAndLogicalChildren = true; + this.InvalidateMeasure(); + } + + #endregion + + #region Methods + + /// + /// Gets current used layout definition (or null if no present definitions) + /// + /// Layout definition or null + internal RibbonToolBarLayoutDefinition GetCurrentLayoutDefinition() + { + if (this.layoutDefinitions.Count == 0) return null; + if (this.layoutDefinitions.Count == 1) return this.layoutDefinitions[0]; + + foreach (RibbonToolBarLayoutDefinition definition in this.layoutDefinitions) + { + if (RibbonProperties.GetSize(definition) == RibbonProperties.GetSize(this)) return definition; + } + + // TODO: try to find a better definition + return this.layoutDefinitions[0]; + } + + #endregion + + #region Size Property Changing + + /// + /// Handles size property changing + /// + /// Previous value + /// Current value + public void OnSizePropertyChanged(RibbonControlSize previous, RibbonControlSize current) + { + foreach (var frameworkElement in this.actualChildren) + { + RibbonProperties.SetSize(frameworkElement, current); + } + + this.rebuildVisualAndLogicalChildren = true; + this.InvalidateMeasure(); + } + + #endregion + + #region Layout Overriding + + /// + /// Measures all of the RibbonGroupBox, and resize them appropriately + /// to fit within the available room + /// + /// The available size that + /// this element can give to child elements. + /// The size that the panel determines it needs during + /// layout, based on its calculations of child element sizes. + /// + protected override Size MeasureOverride(Size availableSize) + { + RibbonToolBarLayoutDefinition layoutDefinition = this.GetCurrentLayoutDefinition(); + + // Rebuilding actual children (visual & logical) + if (this.rebuildVisualAndLogicalChildren) + { + // Clear previous children + foreach (FrameworkElement child in this.actualChildren) + { + RibbonToolBarControlGroup controlGroup = child as RibbonToolBarControlGroup; + if (controlGroup != null) controlGroup.Items.Clear(); + this.RemoveVisualChild(child); + this.RemoveLogicalChild(child); + } + this.actualChildren.Clear(); + this.cachedControlGroups.Clear(); + } + + if (layoutDefinition == null) + { + if (this.rebuildVisualAndLogicalChildren) + { + // If default layout is used add all children + foreach (FrameworkElement child in this.Children) + { + this.actualChildren.Add(child); + this.AddVisualChild(child); + this.AddLogicalChild(child); + } + this.rebuildVisualAndLogicalChildren = false; + } + return this.WrapPanelLayuot(availableSize, true); + } + else + { + Size result = this.CustomLayout(layoutDefinition, availableSize, true, this.rebuildVisualAndLogicalChildren); + this.rebuildVisualAndLogicalChildren = false; + return result; + } + + + } + + /// + /// When overridden in a derived class, positions child elements and determines + /// a size for a System.Windows.FrameworkElement derived class. + /// + /// The final area within the parent that this + /// element should use to arrange itself and its children. + /// The actual size used. + protected override Size ArrangeOverride(Size finalSize) + { + RibbonToolBarLayoutDefinition layoutDefinition = this.GetCurrentLayoutDefinition(); + if (layoutDefinition == null) return this.WrapPanelLayuot(finalSize, false); + return this.CustomLayout(layoutDefinition, finalSize, false, false); + } + + + #region Wrap Panel Layout + + /// + /// Unified method for wrap panel logic + /// + /// Available or final size + /// Pass true if measure required; pass false if arrange required + /// Final size + Size WrapPanelLayuot(Size availableSize, bool measure) + { + bool arrange = !measure; + double availableHeight = double.IsPositiveInfinity(availableSize.Height) ? 0 : availableSize.Height; + + double currentheight = 0; + double columnWidth = 0; + + double resultWidth = 0; + double resultHeight = 0; + + Size infinity = new Size(double.PositiveInfinity, double.PositiveInfinity); + foreach (FrameworkElement child in this.children) + { + // Measuring + if (measure) child.Measure(infinity); + + if (currentheight + child.DesiredSize.Height > availableHeight) + { + // Move to the next column + resultHeight = Math.Max(resultHeight, currentheight); + resultWidth += columnWidth; + currentheight = 0; + columnWidth = 0; + } + + // Arranging + if (arrange) child.Arrange(new Rect( + resultWidth, + currentheight, + child.DesiredSize.Width, + child.DesiredSize.Height)); + + columnWidth = Math.Max(columnWidth, child.DesiredSize.Width); + currentheight += child.DesiredSize.Height; + } + + return new Size(resultWidth + columnWidth, resultHeight); + } + + #endregion + + #region Control and Group Creation from a Definition + + FrameworkElement GetControl(RibbonToolBarControlDefinition controlDefinition) + { + string name = controlDefinition.Target; + return this.Children.FirstOrDefault(x => x.Name == name); + } + + Dictionary cachedControlGroups = new Dictionary(); + RibbonToolBarControlGroup GetControlGroup(RibbonToolBarControlGroupDefinition controlGroupDefinition) + { + RibbonToolBarControlGroup controlGroup = null; + if (!this.cachedControlGroups.TryGetValue(controlGroupDefinition, out controlGroup)) + { + controlGroup = new RibbonToolBarControlGroup(); + // Add items to the group + foreach (RibbonToolBarControlDefinition child in controlGroupDefinition.Children) + { + controlGroup.Items.Add(this.GetControl(child)); + } + this.cachedControlGroups.Add(controlGroupDefinition, controlGroup); + } + return controlGroup; + } + + #endregion + + #region Custom Layout + + // Cached separators (clear & set in Measure pass) + Dictionary separatorCache = new Dictionary(); + + /// + /// Layout logic for the given layout definition + /// + /// Current layout definition + /// Available or final size + /// Pass true if measure required; pass false if arrange required + /// Determines whether we have to add children to the logical and visual tree + /// Final size + Size CustomLayout(RibbonToolBarLayoutDefinition layoutDefinition, Size availableSize, bool measure, bool addchildren) + { + bool arrange = !measure; + double availableHeight = double.IsPositiveInfinity(availableSize.Height) ? 0 : availableSize.Height; + + // Clear separator cahce + if (addchildren) + this.separatorCache.Clear(); + + // Get the first control and measure, its height accepts as row height + double rowHeight = this.GetRowHeight(layoutDefinition); + + + // Calculate whitespace + int rowCountInColumn = Math.Min(layoutDefinition.RowCount, layoutDefinition.Rows.Count); + double whitespace = (availableHeight - ((double)rowCountInColumn * rowHeight)) / (double)(rowCountInColumn + 1); + + double y = 0; + double x = 0; + double currentRowBegin = 0; + double currentMaxX = 0; + double maxy = 0; + for(int rowIndex = 0; rowIndex < layoutDefinition.Rows.Count; rowIndex++) + { + RibbonToolBarRow row = layoutDefinition.Rows[rowIndex]; + + x = currentRowBegin; + + if (rowIndex % rowCountInColumn == 0) + { + // Reset vars at new column + x = currentRowBegin = currentMaxX; + y = 0; + + if (rowIndex != 0) + { + #region Add separator + + Separator separator = null; + if (!this.separatorCache.TryGetValue(rowIndex, out separator)) + { + separator = new Separator(); + separator.Style = this.SeparatorStyle; + this.separatorCache.Add(rowIndex, separator); + } + if (measure) + { + separator.Height = availableHeight - separator.Margin.Bottom - separator.Margin.Top; + separator.Measure(availableSize); + } + if (arrange) separator.Arrange(new Rect(x, y, + separator.DesiredSize.Width, + separator.DesiredSize.Height)); + x += separator.DesiredSize.Width; + + if (addchildren) + { + // Add control in the children + this.AddVisualChild(separator); + this.AddLogicalChild(separator); + this.actualChildren.Add(separator); + } + + #endregion + } + } + y += whitespace; + + + // Measure & arrange new row + for(int i = 0; i < row.Children.Count; i++) + { + if (row.Children[i] is RibbonToolBarControlDefinition) + { + // Control Definition Case + RibbonToolBarControlDefinition controlDefinition = + (RibbonToolBarControlDefinition) row.Children[i]; + FrameworkElement control = this.GetControl(controlDefinition); + if (control == null) continue; + + if (addchildren) + { + // Add control in the children + this.AddVisualChild(control); + this.AddLogicalChild(control); + this.actualChildren.Add(control); + } + + if (measure) + { + // Apply Control Definition Properties + RibbonProperties.SetSize(control, RibbonProperties.GetSize(controlDefinition)); + control.Width = controlDefinition.Width; + control.Measure(availableSize); + } + if (arrange) + { + control.Arrange(new Rect(x, y, + control.DesiredSize.Width, + control.DesiredSize.Height)); + } + + x += control.DesiredSize.Width; + } + if (row.Children[i] is RibbonToolBarControlGroupDefinition) + { + // Control Definition Case + RibbonToolBarControlGroupDefinition controlGroupDefinition = + (RibbonToolBarControlGroupDefinition)row.Children[i]; + + RibbonToolBarControlGroup control = this.GetControlGroup(controlGroupDefinition); + + if (addchildren) + { + // Add control in the children + this.AddVisualChild(control); + this.AddLogicalChild(control); + this.actualChildren.Add(control); + } + + if (measure) + { + // Apply Control Definition Properties + control.IsFirstInRow = (i == 0); + control.IsLastInRow = (i == row.Children.Count - 1); + control.Measure(availableSize); + } + if (arrange) + { + control.Arrange(new Rect(x, y, + control.DesiredSize.Width, + control.DesiredSize.Height)); + } + + x += control.DesiredSize.Width; + } + } + + y += rowHeight; + if (currentMaxX < x) currentMaxX = x; + if (maxy < y) maxy = y; + } + + return new Size(currentMaxX, maxy + whitespace); + } + + // Get the first control and measure, its height accepts as row height + double GetRowHeight(RibbonToolBarLayoutDefinition layoutDefinition) + { + const double defaultRowHeight = 0; + foreach (RibbonToolBarRow row in layoutDefinition.Rows) + { + foreach (DependencyObject item in row.Children) + { + RibbonToolBarControlDefinition controlDefinition = item as RibbonToolBarControlDefinition; + RibbonToolBarControlGroupDefinition controlGroupDefinition = item as RibbonToolBarControlGroupDefinition; + FrameworkElement control = null; + if (controlDefinition != null) control = this.GetControl(controlDefinition); + else if (controlGroupDefinition != null) + control = this.GetControlGroup(controlGroupDefinition); + + if (control == null) return defaultRowHeight; + control.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); + return control.DesiredSize.Height; + } + } + return defaultRowHeight; + } + + #endregion + + #endregion + + #region QAT Support + + // (!) RibbonToolBar must not to be in QAT + + /// + /// Gets control which represents shortcut item. + /// This item MUST be syncronized with the original + /// and send command to original one control. + /// + /// Control which represents shortcut item + public override FrameworkElement CreateQuickAccessItem() + { + return new Control(); + } + + #endregion + } } \ No newline at end of file diff --git a/Fluent/Controls/RibbonToolBarControlDefinition.cs b/Fluent.Ribbon/Controls/RibbonToolBarControlDefinition.cs similarity index 97% rename from Fluent/Controls/RibbonToolBarControlDefinition.cs rename to Fluent.Ribbon/Controls/RibbonToolBarControlDefinition.cs index ed5bcf7a2..5859aa2b0 100644 --- a/Fluent/Controls/RibbonToolBarControlDefinition.cs +++ b/Fluent.Ribbon/Controls/RibbonToolBarControlDefinition.cs @@ -1,142 +1,142 @@ -using System; -using System.ComponentModel; -using System.Windows; - -namespace Fluent -{ - using Fluent.Extensibility; - - /// - /// Represent logical definition for a control in toolbar - /// - public class RibbonToolBarControlDefinition : DependencyObject, INotifyPropertyChanged, IRibbonSizeChangedSink - { - /// - /// Creates a new instance - /// - public RibbonToolBarControlDefinition() - { - RibbonProperties.SetSize(this, RibbonControlSize.Small); - } - - #region Size - - /// - /// Gets or sets Size for the element. - /// - public RibbonControlSize Size - { - get { return (RibbonControlSize)this.GetValue(SizeProperty); } - set { this.SetValue(SizeProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Size. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty SizeProperty = RibbonProperties.SizeProperty.AddOwner(typeof(RibbonToolBarControlDefinition)); - - #endregion - - #region SizeDefinition - - /// - /// Gets or sets SizeDefinition for element. - /// - public RibbonControlSizeDefinition SizeDefinition - { - get { return (RibbonControlSizeDefinition)this.GetValue(SizeDefinitionProperty); } - set { this.SetValue(SizeDefinitionProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for SizeDefinition. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty SizeDefinitionProperty = RibbonProperties.SizeDefinitionProperty.AddOwner(typeof(RibbonToolBarControlDefinition)); - - #endregion - - #region Target Property - - /// - /// Gets or sets name of the target control - /// - public string Target - { - get { return (string)this.GetValue(TargetProperty); } - set { this.SetValue(TargetProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for ControlName. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty TargetProperty = - DependencyProperty.Register("Target", typeof(string), - typeof(RibbonToolBarControlDefinition), new UIPropertyMetadata(null, OnTargetPropertyChanged)); - - static void OnTargetPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - RibbonToolBarControlDefinition definition = (RibbonToolBarControlDefinition) d; - definition.Invalidate("Target"); - } - - #endregion - - #region Width Property - - /// - /// Gets or sets width of the target control - /// - public double Width - { - get { return (double)this.GetValue(WidthProperty); } - set { this.SetValue(WidthProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Width. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty WidthProperty = - DependencyProperty.Register("Width", typeof(double), typeof(RibbonToolBarControlDefinition), new UIPropertyMetadata(double.NaN, OnWidthPropertyChanged)); - - static void OnWidthPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - RibbonToolBarControlDefinition definition = (RibbonToolBarControlDefinition) d; - definition.Invalidate("Width"); - } - - #endregion - - #region Invalidating - - /// - /// Occurs when a property value changes. - /// - public event PropertyChangedEventHandler PropertyChanged; - - private void Invalidate(string propertyName) - { - if (this.PropertyChanged != null) - this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); - } - - #endregion - - #region Implementation of IRibbonSizeChangedSink - - /// - /// Called when the size is changed - /// - /// Size before change - /// Size after change - public void OnSizePropertyChanged(RibbonControlSize previous, RibbonControlSize current) - { - this.Invalidate("Size"); - } - - #endregion - } +using System; +using System.ComponentModel; +using System.Windows; + +namespace Fluent +{ + using Fluent.Extensibility; + + /// + /// Represent logical definition for a control in toolbar + /// + public class RibbonToolBarControlDefinition : DependencyObject, INotifyPropertyChanged, IRibbonSizeChangedSink + { + /// + /// Creates a new instance + /// + public RibbonToolBarControlDefinition() + { + RibbonProperties.SetSize(this, RibbonControlSize.Small); + } + + #region Size + + /// + /// Gets or sets Size for the element. + /// + public RibbonControlSize Size + { + get { return (RibbonControlSize)this.GetValue(SizeProperty); } + set { this.SetValue(SizeProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Size. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty SizeProperty = RibbonProperties.SizeProperty.AddOwner(typeof(RibbonToolBarControlDefinition)); + + #endregion + + #region SizeDefinition + + /// + /// Gets or sets SizeDefinition for element. + /// + public RibbonControlSizeDefinition SizeDefinition + { + get { return (RibbonControlSizeDefinition)this.GetValue(SizeDefinitionProperty); } + set { this.SetValue(SizeDefinitionProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for SizeDefinition. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty SizeDefinitionProperty = RibbonProperties.SizeDefinitionProperty.AddOwner(typeof(RibbonToolBarControlDefinition)); + + #endregion + + #region Target Property + + /// + /// Gets or sets name of the target control + /// + public string Target + { + get { return (string)this.GetValue(TargetProperty); } + set { this.SetValue(TargetProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for ControlName. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty TargetProperty = + DependencyProperty.Register("Target", typeof(string), + typeof(RibbonToolBarControlDefinition), new UIPropertyMetadata(null, OnTargetPropertyChanged)); + + static void OnTargetPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + RibbonToolBarControlDefinition definition = (RibbonToolBarControlDefinition) d; + definition.Invalidate("Target"); + } + + #endregion + + #region Width Property + + /// + /// Gets or sets width of the target control + /// + public double Width + { + get { return (double)this.GetValue(WidthProperty); } + set { this.SetValue(WidthProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Width. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty WidthProperty = + DependencyProperty.Register("Width", typeof(double), typeof(RibbonToolBarControlDefinition), new UIPropertyMetadata(double.NaN, OnWidthPropertyChanged)); + + static void OnWidthPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + RibbonToolBarControlDefinition definition = (RibbonToolBarControlDefinition) d; + definition.Invalidate("Width"); + } + + #endregion + + #region Invalidating + + /// + /// Occurs when a property value changes. + /// + public event PropertyChangedEventHandler PropertyChanged; + + private void Invalidate(string propertyName) + { + if (this.PropertyChanged != null) + this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); + } + + #endregion + + #region Implementation of IRibbonSizeChangedSink + + /// + /// Called when the size is changed + /// + /// Size before change + /// Size after change + public void OnSizePropertyChanged(RibbonControlSize previous, RibbonControlSize current) + { + this.Invalidate("Size"); + } + + #endregion + } } \ No newline at end of file diff --git a/Fluent/Controls/RibbonToolBarControlGroup.cs b/Fluent.Ribbon/Controls/RibbonToolBarControlGroup.cs similarity index 97% rename from Fluent/Controls/RibbonToolBarControlGroup.cs rename to Fluent.Ribbon/Controls/RibbonToolBarControlGroup.cs index a298729a8..7f10e7b9f 100644 --- a/Fluent/Controls/RibbonToolBarControlGroup.cs +++ b/Fluent.Ribbon/Controls/RibbonToolBarControlGroup.cs @@ -1,76 +1,76 @@ -using System; -using System.Collections.ObjectModel; -using System.Collections.Specialized; -using System.ComponentModel; -using System.Diagnostics.CodeAnalysis; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Markup; - -namespace Fluent -{ - /// - /// Represent logical container for toolbar items - /// - [ContentProperty("Children")] - public class RibbonToolBarControlGroup : ItemsControl - { - #region Properties - - /// - /// Gets whether the group is the fisrt control in the row - /// - public bool IsFirstInRow - { - get { return (bool)this.GetValue(IsFirstInRowProperty); } - set { this.SetValue(IsFirstInRowProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for IsFirstInRow. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IsFirstInRowProperty = - DependencyProperty.Register("IsFirstInRow", typeof(bool), typeof(RibbonToolBarControlGroup), new UIPropertyMetadata(true)); - - /// - /// Gets whether the group is the last control in the row - /// - public bool IsLastInRow - { - get { return (bool)this.GetValue(IsLastInRowProperty); } - set { this.SetValue(IsLastInRowProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for IsFirstInRow. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IsLastInRowProperty = - DependencyProperty.Register("IsLastInRow", typeof(bool), typeof(RibbonToolBarControlGroup), new UIPropertyMetadata(true)); - - #endregion - - #region Initialization - - [SuppressMessage("Microsoft.Performance", "CA1810")] - static RibbonToolBarControlGroup() - { - DefaultStyleKeyProperty.OverrideMetadata(typeof(RibbonToolBarControlGroup), new FrameworkPropertyMetadata(typeof(RibbonToolBarControlGroup))); - StyleProperty.OverrideMetadata(typeof(RibbonToolBarControlGroup), new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceStyle))); - } - - // Coerce object style - static object OnCoerceStyle(DependencyObject d, object basevalue) - { - if (basevalue == null) - { - basevalue = (d as FrameworkElement).TryFindResource(typeof(RibbonToolBarControlGroup)); - } - - return basevalue; - } - - #endregion - } +using System; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Markup; + +namespace Fluent +{ + /// + /// Represent logical container for toolbar items + /// + [ContentProperty("Children")] + public class RibbonToolBarControlGroup : ItemsControl + { + #region Properties + + /// + /// Gets whether the group is the fisrt control in the row + /// + public bool IsFirstInRow + { + get { return (bool)this.GetValue(IsFirstInRowProperty); } + set { this.SetValue(IsFirstInRowProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for IsFirstInRow. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IsFirstInRowProperty = + DependencyProperty.Register("IsFirstInRow", typeof(bool), typeof(RibbonToolBarControlGroup), new UIPropertyMetadata(true)); + + /// + /// Gets whether the group is the last control in the row + /// + public bool IsLastInRow + { + get { return (bool)this.GetValue(IsLastInRowProperty); } + set { this.SetValue(IsLastInRowProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for IsFirstInRow. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IsLastInRowProperty = + DependencyProperty.Register("IsLastInRow", typeof(bool), typeof(RibbonToolBarControlGroup), new UIPropertyMetadata(true)); + + #endregion + + #region Initialization + + [SuppressMessage("Microsoft.Performance", "CA1810")] + static RibbonToolBarControlGroup() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(RibbonToolBarControlGroup), new FrameworkPropertyMetadata(typeof(RibbonToolBarControlGroup))); + StyleProperty.OverrideMetadata(typeof(RibbonToolBarControlGroup), new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceStyle))); + } + + // Coerce object style + static object OnCoerceStyle(DependencyObject d, object basevalue) + { + if (basevalue == null) + { + basevalue = (d as FrameworkElement).TryFindResource(typeof(RibbonToolBarControlGroup)); + } + + return basevalue; + } + + #endregion + } } \ No newline at end of file diff --git a/Fluent/Controls/RibbonToolBarControlGroupDefinition.cs b/Fluent.Ribbon/Controls/RibbonToolBarControlGroupDefinition.cs similarity index 96% rename from Fluent/Controls/RibbonToolBarControlGroupDefinition.cs rename to Fluent.Ribbon/Controls/RibbonToolBarControlGroupDefinition.cs index 797b91069..174a3ade2 100644 --- a/Fluent/Controls/RibbonToolBarControlGroupDefinition.cs +++ b/Fluent.Ribbon/Controls/RibbonToolBarControlGroupDefinition.cs @@ -1,63 +1,63 @@ -using System; -using System.Collections.ObjectModel; -using System.Collections.Specialized; -using System.ComponentModel; -using System.Windows; -using System.Windows.Markup; - -namespace Fluent -{ - /// - /// Represent logical container for toolbar items - /// - [ContentProperty("Children")] - public class RibbonToolBarControlGroupDefinition : DependencyObject - { - #region Events - - /// - /// Occures when children has been changed - /// - public event NotifyCollectionChangedEventHandler ChildrenChanged; - - #endregion - - #region Fields - - // User defined rows - readonly ObservableCollection children = new ObservableCollection(); - - #endregion - - #region Children Property - - /// - /// Gets rows - /// - [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] - public ObservableCollection Children - { - get { return this.children; } - } - - #endregion - - #region Initialization - - /// - /// Default constructor - /// - public RibbonToolBarControlGroupDefinition() - { - this.children.CollectionChanged += this.OnChildrenCollectionChanged; - } - - void OnChildrenCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) - { - if (this.ChildrenChanged != null) - this.ChildrenChanged(sender, e); - } - - #endregion - } +using System; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Windows; +using System.Windows.Markup; + +namespace Fluent +{ + /// + /// Represent logical container for toolbar items + /// + [ContentProperty("Children")] + public class RibbonToolBarControlGroupDefinition : DependencyObject + { + #region Events + + /// + /// Occures when children has been changed + /// + public event NotifyCollectionChangedEventHandler ChildrenChanged; + + #endregion + + #region Fields + + // User defined rows + readonly ObservableCollection children = new ObservableCollection(); + + #endregion + + #region Children Property + + /// + /// Gets rows + /// + [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] + public ObservableCollection Children + { + get { return this.children; } + } + + #endregion + + #region Initialization + + /// + /// Default constructor + /// + public RibbonToolBarControlGroupDefinition() + { + this.children.CollectionChanged += this.OnChildrenCollectionChanged; + } + + void OnChildrenCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + if (this.ChildrenChanged != null) + this.ChildrenChanged(sender, e); + } + + #endregion + } } \ No newline at end of file diff --git a/Fluent/Controls/RibbonToolBarLayoutDefinition.cs b/Fluent.Ribbon/Controls/RibbonToolBarLayoutDefinition.cs similarity index 96% rename from Fluent/Controls/RibbonToolBarLayoutDefinition.cs rename to Fluent.Ribbon/Controls/RibbonToolBarLayoutDefinition.cs index cd4b01556..97851dbbd 100644 --- a/Fluent/Controls/RibbonToolBarLayoutDefinition.cs +++ b/Fluent.Ribbon/Controls/RibbonToolBarLayoutDefinition.cs @@ -1,94 +1,94 @@ -using System.Collections.ObjectModel; -using System.ComponentModel; -using System.Windows; -using System.Windows.Markup; - -namespace Fluent -{ - /// - /// Represents size definition for group box - /// - [ContentProperty("Rows")] - public class RibbonToolBarLayoutDefinition : DependencyObject - { - #region Fields - - // User defined rows - ObservableCollection rows = new ObservableCollection(); - - #endregion - - #region Properties - - #region Size - - /// - /// Gets or sets Size for the element. - /// - public RibbonControlSize Size - { - get { return (RibbonControlSize)this.GetValue(SizeProperty); } - set { this.SetValue(SizeProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Size. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty SizeProperty = RibbonProperties.SizeProperty.AddOwner(typeof(RibbonToolBarLayoutDefinition)); - - #endregion - - #region SizeDefinition - - /// - /// Gets or sets SizeDefinition for element. - /// - public RibbonControlSizeDefinition SizeDefinition - { - get { return (RibbonControlSizeDefinition)this.GetValue(SizeDefinitionProperty); } - set { this.SetValue(SizeDefinitionProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for SizeDefinition. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty SizeDefinitionProperty = RibbonProperties.SizeDefinitionProperty.AddOwner(typeof(RibbonToolBarLayoutDefinition)); - - #endregion - - #region Row Count - - /// - /// Gets or sets count of rows in the ribbon toolbar - /// - public int RowCount - { - get { return (int)this.GetValue(RowCountProperty); } - set { this.SetValue(RowCountProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for RowCount. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty RowCountProperty = - DependencyProperty.Register("RowCount", typeof(int), typeof(RibbonToolBar), new UIPropertyMetadata(3)); - - - #endregion - - - /// - /// Gets rows - /// - [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] - public ObservableCollection Rows - { - get { return this.rows; } - } - - #endregion - } -} +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Windows; +using System.Windows.Markup; + +namespace Fluent +{ + /// + /// Represents size definition for group box + /// + [ContentProperty("Rows")] + public class RibbonToolBarLayoutDefinition : DependencyObject + { + #region Fields + + // User defined rows + ObservableCollection rows = new ObservableCollection(); + + #endregion + + #region Properties + + #region Size + + /// + /// Gets or sets Size for the element. + /// + public RibbonControlSize Size + { + get { return (RibbonControlSize)this.GetValue(SizeProperty); } + set { this.SetValue(SizeProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Size. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty SizeProperty = RibbonProperties.SizeProperty.AddOwner(typeof(RibbonToolBarLayoutDefinition)); + + #endregion + + #region SizeDefinition + + /// + /// Gets or sets SizeDefinition for element. + /// + public RibbonControlSizeDefinition SizeDefinition + { + get { return (RibbonControlSizeDefinition)this.GetValue(SizeDefinitionProperty); } + set { this.SetValue(SizeDefinitionProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for SizeDefinition. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty SizeDefinitionProperty = RibbonProperties.SizeDefinitionProperty.AddOwner(typeof(RibbonToolBarLayoutDefinition)); + + #endregion + + #region Row Count + + /// + /// Gets or sets count of rows in the ribbon toolbar + /// + public int RowCount + { + get { return (int)this.GetValue(RowCountProperty); } + set { this.SetValue(RowCountProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for RowCount. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty RowCountProperty = + DependencyProperty.Register("RowCount", typeof(int), typeof(RibbonToolBar), new UIPropertyMetadata(3)); + + + #endregion + + + /// + /// Gets rows + /// + [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] + public ObservableCollection Rows + { + get { return this.rows; } + } + + #endregion + } +} diff --git a/Fluent/Controls/RibbonToolBarRow.cs b/Fluent.Ribbon/Controls/RibbonToolBarRow.cs similarity index 96% rename from Fluent/Controls/RibbonToolBarRow.cs rename to Fluent.Ribbon/Controls/RibbonToolBarRow.cs index 0466af7f7..94890508c 100644 --- a/Fluent/Controls/RibbonToolBarRow.cs +++ b/Fluent.Ribbon/Controls/RibbonToolBarRow.cs @@ -1,46 +1,46 @@ -using System; -using System.Collections.ObjectModel; -using System.ComponentModel; -using System.Diagnostics.CodeAnalysis; -using System.Windows; -using System.Windows.Markup; - -namespace Fluent -{ - /// - /// Represents size definition for group box - /// - [ContentProperty("Children")] - [SuppressMessage("Microsoft.Naming", "CA1702", Justification = "We mean here 'bar row' instead of 'barrow'")] - public class RibbonToolBarRow : DependencyObject - { - #region Fields - - // User defined rows - readonly ObservableCollection children = new ObservableCollection(); - - #endregion - - #region Properties - - /// - /// Gets rows - /// - [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] - public ObservableCollection Children - { - get { return this.children; } - } - - #endregion - - #region Initialization - - /// - /// Default constructor - /// - public RibbonToolBarRow(){} - - #endregion - } -} +using System; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Windows; +using System.Windows.Markup; + +namespace Fluent +{ + /// + /// Represents size definition for group box + /// + [ContentProperty("Children")] + [SuppressMessage("Microsoft.Naming", "CA1702", Justification = "We mean here 'bar row' instead of 'barrow'")] + public class RibbonToolBarRow : DependencyObject + { + #region Fields + + // User defined rows + readonly ObservableCollection children = new ObservableCollection(); + + #endregion + + #region Properties + + /// + /// Gets rows + /// + [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] + public ObservableCollection Children + { + get { return this.children; } + } + + #endregion + + #region Initialization + + /// + /// Default constructor + /// + public RibbonToolBarRow(){} + + #endregion + } +} diff --git a/Fluent/Controls/RibbonWindow.cs b/Fluent.Ribbon/Controls/RibbonWindow.cs similarity index 97% rename from Fluent/Controls/RibbonWindow.cs rename to Fluent.Ribbon/Controls/RibbonWindow.cs index c22ebb4e5..e71a31e37 100644 --- a/Fluent/Controls/RibbonWindow.cs +++ b/Fluent.Ribbon/Controls/RibbonWindow.cs @@ -1,437 +1,437 @@ -namespace Fluent -{ - using System; - using System.Diagnostics.CodeAnalysis; - using System.Windows; - using System.Windows.Data; - using System.Windows.Input; - using System.Windows.Interactivity; - using System.Windows.Interop; - using ControlzEx.Behaviours; - - using Fluent.Extensions; - using Fluent.Metro.Native; - - //using WindowChrome = System.Windows.Shell.WindowChrome; - using WindowChrome = ControlzEx.Microsoft.Windows.Shell.WindowChrome; - - /// - /// Represents basic window for ribbon - /// - [SuppressMessage("Microsoft.Design", "CA1049")] - [TemplatePart(Name = PART_Icon, Type = typeof(UIElement))] - [TemplatePart(Name = PART_ContentPresenter, Type = typeof(UIElement))] - [TemplatePart(Name = PART_WindowCommands, Type = typeof(WindowCommands))] - public class RibbonWindow : Window - { - private const string PART_Icon = "PART_Icon"; - private const string PART_ContentPresenter = "PART_ContentPresenter"; - private const string PART_WindowCommands = "PART_WindowCommands"; - - private FrameworkElement iconImage; - - #region Properties - - /// - /// Using a DependencyProperty as the backing store for WindowCommands. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty WindowCommandsProperty = DependencyProperty.Register("WindowCommands", typeof(WindowCommands), typeof(RibbonWindow), new PropertyMetadata(null)); - - /// - /// Gets or sets the window commands - /// - public WindowCommands WindowCommands - { - get { return (WindowCommands)this.GetValue(WindowCommandsProperty); } - set { this.SetValue(WindowCommandsProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for SaveWindowPosition. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty SavePositionProperty = DependencyProperty.Register("SaveWindowPosition", typeof(bool), typeof(RibbonWindow), new PropertyMetadata(false)); - - /// - /// Gets or sets whether window position will be saved and loaded. - /// - public bool SaveWindowPosition - { - get { return (bool)this.GetValue(SavePositionProperty); } - set { this.SetValue(SavePositionProperty, value); } - } - - /// - /// Gets or sets resize border thickness - /// - public Thickness ResizeBorderThickness - { - get { return (Thickness)this.GetValue(ResizeBorderThicknessProperty); } - set { this.SetValue(ResizeBorderThicknessProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for ResizeBorderTickness. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty ResizeBorderThicknessProperty = - DependencyProperty.Register("ResizeBorderThickness", typeof(Thickness), typeof(RibbonWindow), new UIPropertyMetadata(WindowChromeBehavior.ResizeBorderThicknessProperty.DefaultMetadata.DefaultValue, OnWindowChromeRelevantPropertyChanged)); - - /// - /// Gets or sets glass border thickness - /// - public Thickness GlassFrameThickness - { - get { return (Thickness)this.GetValue(GlassFrameThicknessProperty); } - set { this.SetValue(GlassFrameThicknessProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for GlassFrameThickness. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty GlassFrameThicknessProperty = - DependencyProperty.Register("GlassFrameThickness", typeof(Thickness), typeof(RibbonWindow), new UIPropertyMetadata(new Thickness(0D), OnWindowChromeRelevantPropertyChanged)); - - /// - /// Gets or sets corner radius - /// - public CornerRadius CornerRadius - { - get { return (CornerRadius)this.GetValue(CornerRadiusProperty); } - set { this.SetValue(CornerRadiusProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for CornerRadius. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty CornerRadiusProperty = - DependencyProperty.Register("CornerRadius", typeof(CornerRadius), typeof(RibbonWindow), new UIPropertyMetadata(new CornerRadius(0D), OnWindowChromeRelevantPropertyChanged)); - - /// - /// Using a DependencyProperty as the backing store for DontUseDwm. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty DontUseDwmProperty = - DependencyProperty.Register("DontUseDwm", typeof(bool), typeof(RibbonWindow), new PropertyMetadata(false, OnDontUseDwmChanged)); - - /// - /// Gets or sets whether DWM should be used. - /// - public bool DontUseDwm - { - get { return (bool)this.GetValue(DontUseDwmProperty); } - set { this.SetValue(DontUseDwmProperty, value); } - } - - /// - /// Gets wheter DWM can be used ( is true and is false). - /// - public bool CanUseDwm - { - get { return (bool)this.GetValue(CanUseDwmProperty); } - private set { this.SetValue(CanUseDwmPropertyKey, value); } - } - - private static readonly DependencyPropertyKey CanUseDwmPropertyKey = DependencyProperty.RegisterReadOnly("CanUseDwm", typeof(bool), typeof(RibbonWindow), new PropertyMetadata(true)); - - /// - /// Using a DependencyProperty as the backing store for CanUseDwm. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty CanUseDwmProperty = CanUseDwmPropertyKey.DependencyProperty; - - /// - /// Gets or sets whether icon is visible - /// - public bool IsIconVisible - { - get { return (bool)this.GetValue(IsIconVisibleProperty); } - set { this.SetValue(IsIconVisibleProperty, value); } - } - - /// - /// Gets or sets whether icon is visible - /// - public static readonly DependencyProperty IsIconVisibleProperty = DependencyProperty.Register("IsIconVisible", typeof(bool), typeof(RibbonWindow), new UIPropertyMetadata(true)); - - // todo check if IsCollapsed and IsAutomaticCollapseEnabled should be reduced to one shared property for RibbonWindow and Ribbon - /// - /// Gets whether window is collapsed - /// - public bool IsCollapsed - { - get { return (bool)this.GetValue(IsCollapsedProperty); } - set { this.SetValue(IsCollapsedProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for IsCollapsed. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IsCollapsedProperty = - DependencyProperty.Register("IsCollapsed", typeof(bool), - typeof(RibbonWindow), new FrameworkPropertyMetadata(false)); - - /// - /// Defines if the Ribbon should automatically set when the width or height of the owner window drop under or - /// - public bool IsAutomaticCollapseEnabled - { - get { return (bool)this.GetValue(IsAutomaticCollapseEnabledProperty); } - set { this.SetValue(IsAutomaticCollapseEnabledProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for IsCollapsed. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IsAutomaticCollapseEnabledProperty = - DependencyProperty.Register("IsAutomaticCollapseEnabled", typeof(bool), typeof(RibbonWindow), new PropertyMetadata(true)); - - #endregion - - #region Constructors - - /// - /// Static constructor - /// - static RibbonWindow() - { - StyleProperty.OverrideMetadata(typeof(RibbonWindow), new FrameworkPropertyMetadata(null, OnCoerceStyle)); - DefaultStyleKeyProperty.OverrideMetadata(typeof(RibbonWindow), new FrameworkPropertyMetadata(typeof(RibbonWindow))); - - RibbonProperties.TitleBarHeightProperty.OverrideMetadata(typeof(RibbonWindow), new FrameworkPropertyMetadata(OnWindowChromeRelevantPropertyChanged)); - } - - // Coerce object style - private static object OnCoerceStyle(DependencyObject d, object basevalue) - { - if (basevalue != null) - { - return basevalue; - } - - var frameworkElement = d as FrameworkElement; - if (frameworkElement != null) - { - basevalue = frameworkElement.TryFindResource(typeof(RibbonWindow)); - } - - return basevalue; - } - - /// - /// Default constructor - /// - public RibbonWindow() - { - this.SizeChanged += this.OnSizeChanged; - } - - #endregion - - #region Overrides - - /// - /// Raises the event. - /// - /// An that contains the event data. - protected override void OnSourceInitialized(EventArgs e) - { - base.OnSourceInitialized(e); - - this.UpdateCanUseDwm(); - - this.InitializeWindowChromeBehavior(); - } - - protected virtual void InitializeWindowChromeBehavior() - { - var behavior = new WindowChromeBehavior(); - BindingOperations.SetBinding(behavior, WindowChromeBehavior.CaptionHeightProperty, new Binding { Path = new PropertyPath(RibbonProperties.TitleBarHeightProperty), Source = this }); - BindingOperations.SetBinding(behavior, WindowChromeBehavior.ResizeBorderThicknessProperty, new Binding { Path = new PropertyPath(ResizeBorderThicknessProperty), Source = this }); - BindingOperations.SetBinding(behavior, WindowChromeBehavior.CornerRadiusProperty, new Binding { Path = new PropertyPath(CornerRadiusProperty), Source = this }); - BindingOperations.SetBinding(behavior, WindowChromeBehavior.GlassFrameThicknessProperty, new Binding { Path = new PropertyPath(GlassFrameThicknessProperty), Source = this }); - BindingOperations.SetBinding(behavior, WindowChromeBehavior.UseAeroCaptionButtonsProperty, new Binding { Path = new PropertyPath(CanUseDwmProperty), Source = this }); - Interaction.GetBehaviors(this).Add(behavior); - } - - /// - /// Called when the property changes. - /// - /// A reference to the root of the old content tree.A reference to the root of the new content tree. - protected override void OnContentChanged(object oldContent, object newContent) - { - base.OnContentChanged(oldContent, newContent); - - var content = newContent as IInputElement; - - if (content != null) - { - WindowChrome.SetIsHitTestVisibleInChrome(content, true); - } - } - - #endregion - - private static void OnWindowChromeRelevantPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var window = d as RibbonWindow; - - if (window == null) - { - return; - } - } - - private static void OnDontUseDwmChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var window = d as RibbonWindow; - - if (window == null) - { - return; - } - - window.UpdateCanUseDwm(); - } - - // Size change to collapse ribbon - private void OnSizeChanged(object sender, SizeChangedEventArgs e) - { - this.MaintainIsCollapsed(); - } - - private void MaintainIsCollapsed() - { - if (this.IsAutomaticCollapseEnabled == false) - { - return; - } - - if (this.ActualWidth < Ribbon.MinimalVisibleWidth - || this.ActualHeight < Ribbon.MinimalVisibleHeight) - { - this.IsCollapsed = true; - } - else - { - this.IsCollapsed = false; - } - } - - private void UpdateCanUseDwm() - { - this.CanUseDwm = NativeMethods.IsDwmEnabled() - && this.DontUseDwm == false; - } - - #region Metro - - /// - /// When overridden in a derived class, is invoked whenever application code or internal processes call . - /// - public override void OnApplyTemplate() - { - base.OnApplyTemplate(); - - this.UpdateCanUseDwm(); - - if (this.iconImage != null) - { - this.iconImage.MouseDown -= this.HandleIconMouseDown; - } - - if (this.WindowCommands == null) - { - this.WindowCommands = new WindowCommands(); - } - - this.iconImage = this.GetTemplateChild(PART_Icon) as FrameworkElement; - - if (this.iconImage != null) - { - WindowChrome.SetIsHitTestVisibleInChrome(this.iconImage, true); - - this.iconImage.MouseDown += this.HandleIconMouseDown; - } - - var partContentPresenter = this.GetTemplateChild(PART_ContentPresenter) as UIElement; - - if (partContentPresenter != null) - { - WindowChrome.SetIsHitTestVisibleInChrome(partContentPresenter, true); - } - - var partWindowCommands = this.GetTemplateChild(PART_WindowCommands) as UIElement; - - if (partWindowCommands != null) - { - WindowChrome.SetIsHitTestVisibleInChrome(partWindowCommands, true); - } - } - - /// - /// Raises the event. - /// - /// An that contains the event data. - protected override void OnStateChanged(EventArgs e) - { - if (this.WindowCommands != null) - { - this.WindowCommands.RefreshMaximizeIconState(); - } - - base.OnStateChanged(e); - } - - private void HandleIconMouseDown(object sender, MouseButtonEventArgs e) - { - if (e.ChangedButton == MouseButton.Left) - { - if (e.ClickCount == 1) - { - e.Handled = true; - - ShowSystemMenuPhysicalCoordinates(this, this.PointToScreen(new Point(0, RibbonProperties.GetTitleBarHeight(this)))); - } - else if (e.ClickCount == 2) - { - e.Handled = true; - - this.Close(); - } - } - else if (e.ChangedButton == MouseButton.Right) - { - e.Handled = true; - - this.RunInDispatcherAsync(() => - { - var mousePosition = e.GetPosition(this); - ShowSystemMenuPhysicalCoordinates(this, this.PointToScreen(mousePosition)); - }); - } - } - - private static void ShowSystemMenuPhysicalCoordinates(Window window, Point physicalScreenLocation) - { - if (window == null) - { - return; - } - - var hwnd = new WindowInteropHelper(window).Handle; - if (hwnd == IntPtr.Zero || !UnsafeNativeMethods.IsWindow(hwnd)) - { - return; - } - - var hmenu = UnsafeNativeMethods.GetSystemMenu(hwnd, false); - - var cmd = UnsafeNativeMethods.TrackPopupMenuEx(hmenu, Constants.TPM_LEFTBUTTON | Constants.TPM_RETURNCMD, (int)physicalScreenLocation.X, (int)physicalScreenLocation.Y, hwnd, IntPtr.Zero); - if (0 != cmd) - { - UnsafeNativeMethods.PostMessage(hwnd, Constants.SYSCOMMAND, new IntPtr(cmd), IntPtr.Zero); - } - } - - #endregion - } +namespace Fluent +{ + using System; + using System.Diagnostics.CodeAnalysis; + using System.Windows; + using System.Windows.Data; + using System.Windows.Input; + using System.Windows.Interactivity; + using System.Windows.Interop; + using ControlzEx.Behaviours; + + using Fluent.Extensions; + using Fluent.Metro.Native; + + //using WindowChrome = System.Windows.Shell.WindowChrome; + using WindowChrome = ControlzEx.Microsoft.Windows.Shell.WindowChrome; + + /// + /// Represents basic window for ribbon + /// + [SuppressMessage("Microsoft.Design", "CA1049")] + [TemplatePart(Name = PART_Icon, Type = typeof(UIElement))] + [TemplatePart(Name = PART_ContentPresenter, Type = typeof(UIElement))] + [TemplatePart(Name = PART_WindowCommands, Type = typeof(WindowCommands))] + public class RibbonWindow : Window + { + private const string PART_Icon = "PART_Icon"; + private const string PART_ContentPresenter = "PART_ContentPresenter"; + private const string PART_WindowCommands = "PART_WindowCommands"; + + private FrameworkElement iconImage; + + #region Properties + + /// + /// Using a DependencyProperty as the backing store for WindowCommands. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty WindowCommandsProperty = DependencyProperty.Register("WindowCommands", typeof(WindowCommands), typeof(RibbonWindow), new PropertyMetadata(null)); + + /// + /// Gets or sets the window commands + /// + public WindowCommands WindowCommands + { + get { return (WindowCommands)this.GetValue(WindowCommandsProperty); } + set { this.SetValue(WindowCommandsProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for SaveWindowPosition. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty SavePositionProperty = DependencyProperty.Register("SaveWindowPosition", typeof(bool), typeof(RibbonWindow), new PropertyMetadata(false)); + + /// + /// Gets or sets whether window position will be saved and loaded. + /// + public bool SaveWindowPosition + { + get { return (bool)this.GetValue(SavePositionProperty); } + set { this.SetValue(SavePositionProperty, value); } + } + + /// + /// Gets or sets resize border thickness + /// + public Thickness ResizeBorderThickness + { + get { return (Thickness)this.GetValue(ResizeBorderThicknessProperty); } + set { this.SetValue(ResizeBorderThicknessProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for ResizeBorderTickness. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty ResizeBorderThicknessProperty = + DependencyProperty.Register("ResizeBorderThickness", typeof(Thickness), typeof(RibbonWindow), new UIPropertyMetadata(WindowChromeBehavior.ResizeBorderThicknessProperty.DefaultMetadata.DefaultValue, OnWindowChromeRelevantPropertyChanged)); + + /// + /// Gets or sets glass border thickness + /// + public Thickness GlassFrameThickness + { + get { return (Thickness)this.GetValue(GlassFrameThicknessProperty); } + set { this.SetValue(GlassFrameThicknessProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for GlassFrameThickness. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty GlassFrameThicknessProperty = + DependencyProperty.Register("GlassFrameThickness", typeof(Thickness), typeof(RibbonWindow), new UIPropertyMetadata(new Thickness(0D), OnWindowChromeRelevantPropertyChanged)); + + /// + /// Gets or sets corner radius + /// + public CornerRadius CornerRadius + { + get { return (CornerRadius)this.GetValue(CornerRadiusProperty); } + set { this.SetValue(CornerRadiusProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for CornerRadius. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty CornerRadiusProperty = + DependencyProperty.Register("CornerRadius", typeof(CornerRadius), typeof(RibbonWindow), new UIPropertyMetadata(new CornerRadius(0D), OnWindowChromeRelevantPropertyChanged)); + + /// + /// Using a DependencyProperty as the backing store for DontUseDwm. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty DontUseDwmProperty = + DependencyProperty.Register("DontUseDwm", typeof(bool), typeof(RibbonWindow), new PropertyMetadata(false, OnDontUseDwmChanged)); + + /// + /// Gets or sets whether DWM should be used. + /// + public bool DontUseDwm + { + get { return (bool)this.GetValue(DontUseDwmProperty); } + set { this.SetValue(DontUseDwmProperty, value); } + } + + /// + /// Gets wheter DWM can be used ( is true and is false). + /// + public bool CanUseDwm + { + get { return (bool)this.GetValue(CanUseDwmProperty); } + private set { this.SetValue(CanUseDwmPropertyKey, value); } + } + + private static readonly DependencyPropertyKey CanUseDwmPropertyKey = DependencyProperty.RegisterReadOnly("CanUseDwm", typeof(bool), typeof(RibbonWindow), new PropertyMetadata(true)); + + /// + /// Using a DependencyProperty as the backing store for CanUseDwm. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty CanUseDwmProperty = CanUseDwmPropertyKey.DependencyProperty; + + /// + /// Gets or sets whether icon is visible + /// + public bool IsIconVisible + { + get { return (bool)this.GetValue(IsIconVisibleProperty); } + set { this.SetValue(IsIconVisibleProperty, value); } + } + + /// + /// Gets or sets whether icon is visible + /// + public static readonly DependencyProperty IsIconVisibleProperty = DependencyProperty.Register("IsIconVisible", typeof(bool), typeof(RibbonWindow), new UIPropertyMetadata(true)); + + // todo check if IsCollapsed and IsAutomaticCollapseEnabled should be reduced to one shared property for RibbonWindow and Ribbon + /// + /// Gets whether window is collapsed + /// + public bool IsCollapsed + { + get { return (bool)this.GetValue(IsCollapsedProperty); } + set { this.SetValue(IsCollapsedProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for IsCollapsed. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IsCollapsedProperty = + DependencyProperty.Register("IsCollapsed", typeof(bool), + typeof(RibbonWindow), new FrameworkPropertyMetadata(false)); + + /// + /// Defines if the Ribbon should automatically set when the width or height of the owner window drop under or + /// + public bool IsAutomaticCollapseEnabled + { + get { return (bool)this.GetValue(IsAutomaticCollapseEnabledProperty); } + set { this.SetValue(IsAutomaticCollapseEnabledProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for IsCollapsed. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IsAutomaticCollapseEnabledProperty = + DependencyProperty.Register("IsAutomaticCollapseEnabled", typeof(bool), typeof(RibbonWindow), new PropertyMetadata(true)); + + #endregion + + #region Constructors + + /// + /// Static constructor + /// + static RibbonWindow() + { + StyleProperty.OverrideMetadata(typeof(RibbonWindow), new FrameworkPropertyMetadata(null, OnCoerceStyle)); + DefaultStyleKeyProperty.OverrideMetadata(typeof(RibbonWindow), new FrameworkPropertyMetadata(typeof(RibbonWindow))); + + RibbonProperties.TitleBarHeightProperty.OverrideMetadata(typeof(RibbonWindow), new FrameworkPropertyMetadata(OnWindowChromeRelevantPropertyChanged)); + } + + // Coerce object style + private static object OnCoerceStyle(DependencyObject d, object basevalue) + { + if (basevalue != null) + { + return basevalue; + } + + var frameworkElement = d as FrameworkElement; + if (frameworkElement != null) + { + basevalue = frameworkElement.TryFindResource(typeof(RibbonWindow)); + } + + return basevalue; + } + + /// + /// Default constructor + /// + public RibbonWindow() + { + this.SizeChanged += this.OnSizeChanged; + } + + #endregion + + #region Overrides + + /// + /// Raises the event. + /// + /// An that contains the event data. + protected override void OnSourceInitialized(EventArgs e) + { + base.OnSourceInitialized(e); + + this.UpdateCanUseDwm(); + + this.InitializeWindowChromeBehavior(); + } + + protected virtual void InitializeWindowChromeBehavior() + { + var behavior = new WindowChromeBehavior(); + BindingOperations.SetBinding(behavior, WindowChromeBehavior.CaptionHeightProperty, new Binding { Path = new PropertyPath(RibbonProperties.TitleBarHeightProperty), Source = this }); + BindingOperations.SetBinding(behavior, WindowChromeBehavior.ResizeBorderThicknessProperty, new Binding { Path = new PropertyPath(ResizeBorderThicknessProperty), Source = this }); + BindingOperations.SetBinding(behavior, WindowChromeBehavior.CornerRadiusProperty, new Binding { Path = new PropertyPath(CornerRadiusProperty), Source = this }); + BindingOperations.SetBinding(behavior, WindowChromeBehavior.GlassFrameThicknessProperty, new Binding { Path = new PropertyPath(GlassFrameThicknessProperty), Source = this }); + BindingOperations.SetBinding(behavior, WindowChromeBehavior.UseAeroCaptionButtonsProperty, new Binding { Path = new PropertyPath(CanUseDwmProperty), Source = this }); + Interaction.GetBehaviors(this).Add(behavior); + } + + /// + /// Called when the property changes. + /// + /// A reference to the root of the old content tree.A reference to the root of the new content tree. + protected override void OnContentChanged(object oldContent, object newContent) + { + base.OnContentChanged(oldContent, newContent); + + var content = newContent as IInputElement; + + if (content != null) + { + WindowChrome.SetIsHitTestVisibleInChrome(content, true); + } + } + + #endregion + + private static void OnWindowChromeRelevantPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var window = d as RibbonWindow; + + if (window == null) + { + return; + } + } + + private static void OnDontUseDwmChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var window = d as RibbonWindow; + + if (window == null) + { + return; + } + + window.UpdateCanUseDwm(); + } + + // Size change to collapse ribbon + private void OnSizeChanged(object sender, SizeChangedEventArgs e) + { + this.MaintainIsCollapsed(); + } + + private void MaintainIsCollapsed() + { + if (this.IsAutomaticCollapseEnabled == false) + { + return; + } + + if (this.ActualWidth < Ribbon.MinimalVisibleWidth + || this.ActualHeight < Ribbon.MinimalVisibleHeight) + { + this.IsCollapsed = true; + } + else + { + this.IsCollapsed = false; + } + } + + private void UpdateCanUseDwm() + { + this.CanUseDwm = NativeMethods.IsDwmEnabled() + && this.DontUseDwm == false; + } + + #region Metro + + /// + /// When overridden in a derived class, is invoked whenever application code or internal processes call . + /// + public override void OnApplyTemplate() + { + base.OnApplyTemplate(); + + this.UpdateCanUseDwm(); + + if (this.iconImage != null) + { + this.iconImage.MouseDown -= this.HandleIconMouseDown; + } + + if (this.WindowCommands == null) + { + this.WindowCommands = new WindowCommands(); + } + + this.iconImage = this.GetTemplateChild(PART_Icon) as FrameworkElement; + + if (this.iconImage != null) + { + WindowChrome.SetIsHitTestVisibleInChrome(this.iconImage, true); + + this.iconImage.MouseDown += this.HandleIconMouseDown; + } + + var partContentPresenter = this.GetTemplateChild(PART_ContentPresenter) as UIElement; + + if (partContentPresenter != null) + { + WindowChrome.SetIsHitTestVisibleInChrome(partContentPresenter, true); + } + + var partWindowCommands = this.GetTemplateChild(PART_WindowCommands) as UIElement; + + if (partWindowCommands != null) + { + WindowChrome.SetIsHitTestVisibleInChrome(partWindowCommands, true); + } + } + + /// + /// Raises the event. + /// + /// An that contains the event data. + protected override void OnStateChanged(EventArgs e) + { + if (this.WindowCommands != null) + { + this.WindowCommands.RefreshMaximizeIconState(); + } + + base.OnStateChanged(e); + } + + private void HandleIconMouseDown(object sender, MouseButtonEventArgs e) + { + if (e.ChangedButton == MouseButton.Left) + { + if (e.ClickCount == 1) + { + e.Handled = true; + + ShowSystemMenuPhysicalCoordinates(this, this.PointToScreen(new Point(0, RibbonProperties.GetTitleBarHeight(this)))); + } + else if (e.ClickCount == 2) + { + e.Handled = true; + + this.Close(); + } + } + else if (e.ChangedButton == MouseButton.Right) + { + e.Handled = true; + + this.RunInDispatcherAsync(() => + { + var mousePosition = e.GetPosition(this); + ShowSystemMenuPhysicalCoordinates(this, this.PointToScreen(mousePosition)); + }); + } + } + + private static void ShowSystemMenuPhysicalCoordinates(Window window, Point physicalScreenLocation) + { + if (window == null) + { + return; + } + + var hwnd = new WindowInteropHelper(window).Handle; + if (hwnd == IntPtr.Zero || !UnsafeNativeMethods.IsWindow(hwnd)) + { + return; + } + + var hmenu = UnsafeNativeMethods.GetSystemMenu(hwnd, false); + + var cmd = UnsafeNativeMethods.TrackPopupMenuEx(hmenu, Constants.TPM_LEFTBUTTON | Constants.TPM_RETURNCMD, (int)physicalScreenLocation.X, (int)physicalScreenLocation.Y, hwnd, IntPtr.Zero); + if (0 != cmd) + { + UnsafeNativeMethods.PostMessage(hwnd, Constants.SYSCOMMAND, new IntPtr(cmd), IntPtr.Zero); + } + } + + #endregion + } } \ No newline at end of file diff --git a/Fluent/Controls/ScreenTip.cs b/Fluent.Ribbon/Controls/ScreenTip.cs similarity index 97% rename from Fluent/Controls/ScreenTip.cs rename to Fluent.Ribbon/Controls/ScreenTip.cs index 64250b056..99350bae6 100644 --- a/Fluent/Controls/ScreenTip.cs +++ b/Fluent.Ribbon/Controls/ScreenTip.cs @@ -1,429 +1,429 @@ -namespace Fluent -{ - using System; - using System.Diagnostics.CodeAnalysis; - using System.Windows; - using System.Windows.Media; - using System.Windows.Controls; - using System.Windows.Documents; - using System.Windows.Controls.Primitives; - using System.Windows.Input; - - /// - /// ScreenTips display the name of the control, - /// the keyboard shortcut for the control, and a brief description - /// of how to use the control. ScreenTips also can provide F1 support, - /// which opens help and takes the user directly to the related - /// help topic for the control whose ScreenTip was - /// displayed when the F1 button was pressed - /// - public class ScreenTip : ToolTip - { - #region Initialization - - /// - /// Static constructor - /// - [SuppressMessage("Microsoft.Performance", "CA1810")] - static ScreenTip() - { - DefaultStyleKeyProperty.OverrideMetadata(typeof(ScreenTip), new FrameworkPropertyMetadata(typeof(ScreenTip))); - StyleProperty.OverrideMetadata(typeof(ScreenTip), new FrameworkPropertyMetadata(null, OnCoerceStyle)); - } - - private static object OnCoerceStyle(DependencyObject d, object basevalue) - { - if (basevalue == null) - { - var frameworkElement = d as FrameworkElement; - if (frameworkElement != null) - { - basevalue = frameworkElement.TryFindResource(typeof(ScreenTip)); - } - } - - return basevalue; - } - - /// - /// Default constructor - /// - public ScreenTip() - { - this.Opened += this.OnToolTipOpened; - this.Closed += this.OnToolTipClosed; - this.CustomPopupPlacementCallback = this.CustomPopupPlacementMethod; - this.Placement = PlacementMode.Custom; - this.HelpLabelVisibility = Visibility.Visible; - } - - #endregion - - #region Popup Custom Placement - - // Calculate two variants: below and upper ribbon - CustomPopupPlacement[] CustomPopupPlacementMethod(Size popupSize, Size targetSize, Point offset) - { - if (this.PlacementTarget == null) - { - return new CustomPopupPlacement[] { }; - } - - Ribbon ribbon = null; - UIElement topLevelElement = null; - FindControls(this.PlacementTarget, ref ribbon, ref topLevelElement); - - // Exclude QAT items - var notQuickAccessItem = !IsQuickAccessItem(this.PlacementTarget); - var notContextMenuChild = !IsContextMenuChild(this.PlacementTarget); - var rightToLeftOffset = this.FlowDirection == FlowDirection.RightToLeft - ? -popupSize.Width - : 0; - - var decoratorChild = GetDecoratorChild(topLevelElement); - - if (notQuickAccessItem - && this.IsRibbonAligned - && ribbon != null) - { - var belowY = ribbon.TranslatePoint(new Point(0, ribbon.ActualHeight), this.PlacementTarget).Y; - var aboveY = ribbon.TranslatePoint(new Point(0, 0), this.PlacementTarget).Y - popupSize.Height; - var below = new CustomPopupPlacement(new Point(rightToLeftOffset, belowY + 1), PopupPrimaryAxis.Horizontal); - var above = new CustomPopupPlacement(new Point(rightToLeftOffset, aboveY - 1), PopupPrimaryAxis.Horizontal); - return new[] { below, above }; - } - - if (notQuickAccessItem - && this.IsRibbonAligned - && notContextMenuChild - && topLevelElement is Window == false - && decoratorChild != null) - { - // Placed on Popup? - var belowY = decoratorChild.TranslatePoint(new Point(0, ((FrameworkElement)decoratorChild).ActualHeight), this.PlacementTarget).Y; - var aboveY = decoratorChild.TranslatePoint(new Point(0, 0), this.PlacementTarget).Y - popupSize.Height; - var below = new CustomPopupPlacement(new Point(rightToLeftOffset, belowY + 1), PopupPrimaryAxis.Horizontal); - var above = new CustomPopupPlacement(new Point(rightToLeftOffset, aboveY - 1), PopupPrimaryAxis.Horizontal); - return new[] { below, above }; - } - - return new[] { - new CustomPopupPlacement(new Point(rightToLeftOffset, this.PlacementTarget.RenderSize.Height + 1), PopupPrimaryAxis.Horizontal), - new CustomPopupPlacement(new Point(rightToLeftOffset, -popupSize.Height - 1), PopupPrimaryAxis.Horizontal)}; - } - - private static bool IsContextMenuChild(UIElement element) - { - do - { - var parent = VisualTreeHelper.GetParent(element) as UIElement; - //if (parent is ContextMenuBar) return true; - element = parent; - } while (element != null); - - return false; - } - - private static bool IsQuickAccessItem(UIElement element) - { - do - { - var parent = VisualTreeHelper.GetParent(element) as UIElement; - if (parent is QuickAccessToolBar) - { - return true; - } - - element = parent; - } - while (element != null); - - return false; - } - - private static UIElement GetDecoratorChild(UIElement popupRoot) - { - if (popupRoot == null) - { - return null; - } - - var decorator = popupRoot as AdornerDecorator; - if (decorator != null) - { - return decorator.Child; - } - - for (var i = 0; i < VisualTreeHelper.GetChildrenCount(popupRoot); i++) - { - var element = GetDecoratorChild(VisualTreeHelper.GetChild(popupRoot, i) as UIElement); - if (element != null) - { - return element; - } - } - - return null; - } - - private static void FindControls(UIElement obj, ref Ribbon ribbon, ref UIElement topLevelElement) - { - if (obj == null) - { - return; - } - - var objRibbon = obj as Ribbon; - if (objRibbon != null) - { - ribbon = objRibbon; - } - - var parentVisual = VisualTreeHelper.GetParent(obj) as UIElement; - if (parentVisual == null) - { - topLevelElement = obj; - } - else - { - FindControls(parentVisual, ref ribbon, ref topLevelElement); - } - } - - #endregion - - #region Title Property - - /// - /// Gets or sets title of the screen tip - /// - [System.ComponentModel.DisplayName("Title"), - System.ComponentModel.Category("Screen Tip"), - System.ComponentModel.Description("Title of the screen tip")] - public string Title - { - get { return (string)this.GetValue(TitleProperty); } - set { this.SetValue(TitleProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Title. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty TitleProperty = - DependencyProperty.Register("Title", typeof(string), typeof(ScreenTip), new UIPropertyMetadata("")); - - #endregion - - #region Text Property - - /// - /// Gets or sets text of the screen tip - /// - [System.ComponentModel.DisplayName("Text"), - System.ComponentModel.Category("Screen Tip"), - System.ComponentModel.Description("Main text of the screen tip")] - public string Text - { - get { return (string)this.GetValue(TextProperty); } - set { this.SetValue(TextProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Text. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty TextProperty = - DependencyProperty.Register("Text", typeof(string), typeof(ScreenTip), new UIPropertyMetadata("")); - - #endregion - - #region DisableReason Property - - /// - /// Gets or sets disable reason of the associated screen tip's control - /// - [System.ComponentModel.DisplayName("Disable Reason"), - System.ComponentModel.Category("Screen Tip"), - System.ComponentModel.Description("Describe here what would cause disable of the control")] - public string DisableReason - { - get { return (string)this.GetValue(DisableReasonProperty); } - set { this.SetValue(DisableReasonProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for DisableReason. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty DisableReasonProperty = - DependencyProperty.Register("DisableReason", typeof(string), typeof(ScreenTip), new UIPropertyMetadata("")); - - #endregion - - #region HelpTopic Property - - /// - /// Gets or sets help topic of the ScreenTip - /// - [System.ComponentModel.DisplayName("Help Topic"), - System.ComponentModel.Category("Screen Tip"), - System.ComponentModel.Description("Help topic (it will be used to execute help)")] - public object HelpTopic - { - get { return (object)this.GetValue(HelpTopicProperty); } - set { this.SetValue(HelpTopicProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for HelpTopic. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty HelpTopicProperty = - DependencyProperty.Register("HelpTopic", typeof(object), typeof(ScreenTip), new UIPropertyMetadata(null)); - - #endregion - - #region Image Property - - /// - /// Gets or sets image of the screen tip - /// - [System.ComponentModel.DisplayName("Image"), - System.ComponentModel.Category("Screen Tip"), - System.ComponentModel.Description("Image of the screen tip")] - public ImageSource Image - { - get { return (ImageSource)this.GetValue(ImageProperty); } - set { this.SetValue(ImageProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Image. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty ImageProperty = - DependencyProperty.Register("Image", typeof(ImageSource), typeof(ScreenTip), new UIPropertyMetadata(null)); - - #endregion - - #region ShowHelp Property - /// - /// Shows or hides the Help Label - /// - [System.ComponentModel.DisplayName("HelpLabelVisibility"), - System.ComponentModel.Category("Screen Tip"), - System.ComponentModel.Description("Sets the visibility of the F1 Help Label")] - public Visibility HelpLabelVisibility - { - get { return (Visibility)this.GetValue(HelpLabelVisibilityProperty); } - set { this.SetValue(HelpLabelVisibilityProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store the boolean. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty HelpLabelVisibilityProperty = - DependencyProperty.Register("HelpLabelVisibility", typeof(Visibility), typeof(ScreenTip), new UIPropertyMetadata(Visibility.Visible)); - #endregion - - #region Help Invocation - - /// - /// Occurs when user press F1 on ScreenTip with HelpTopic filled - /// - public static event EventHandler HelpPressed; - - #endregion - - #region IsRibbonAligned - - /// - /// Gets or set whether ScreenTip should positioned below Ribbon - /// - public bool IsRibbonAligned - { - get { return (bool)this.GetValue(IsRibbonAlignedProperty); } - set { this.SetValue(IsRibbonAlignedProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for BelowRibbon. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IsRibbonAlignedProperty = - DependencyProperty.Register("BelowRibbon", typeof(bool), typeof(ScreenTip), - new UIPropertyMetadata(true)); - - - #endregion - - #region F1 Help Handling - - // Currently focused element - private IInputElement focusedElement; - - private void OnToolTipClosed(object sender, RoutedEventArgs e) - { - if (this.focusedElement == null) - { - return; - } - - this.focusedElement.PreviewKeyDown -= this.OnFocusedElementPreviewKeyDown; - this.focusedElement = null; - } - - private void OnToolTipOpened(object sender, RoutedEventArgs e) - { - if (this.HelpTopic == null) - { - return; - } - - this.focusedElement = Keyboard.FocusedElement; - if (this.focusedElement != null) - { - this.focusedElement.PreviewKeyDown += this.OnFocusedElementPreviewKeyDown; - } - } - - void OnFocusedElementPreviewKeyDown(object sender, KeyEventArgs e) - { - if (e.Key != Key.F1) - { - return; - } - - e.Handled = true; - - if (HelpPressed != null) - { - HelpPressed(null, new ScreenTipHelpEventArgs(this.HelpTopic)); - } - } - - #endregion - } - - /// - /// Event args for HelpPressed event handler - /// - public class ScreenTipHelpEventArgs : EventArgs - { - /// - /// Gets help topic associated with screen tip - /// - public object HelpTopic { get; private set; } - - /// - /// Constructor - /// - /// Help topic - public ScreenTipHelpEventArgs(object helpTopic) - { - this.HelpTopic = helpTopic; - } - } +namespace Fluent +{ + using System; + using System.Diagnostics.CodeAnalysis; + using System.Windows; + using System.Windows.Media; + using System.Windows.Controls; + using System.Windows.Documents; + using System.Windows.Controls.Primitives; + using System.Windows.Input; + + /// + /// ScreenTips display the name of the control, + /// the keyboard shortcut for the control, and a brief description + /// of how to use the control. ScreenTips also can provide F1 support, + /// which opens help and takes the user directly to the related + /// help topic for the control whose ScreenTip was + /// displayed when the F1 button was pressed + /// + public class ScreenTip : ToolTip + { + #region Initialization + + /// + /// Static constructor + /// + [SuppressMessage("Microsoft.Performance", "CA1810")] + static ScreenTip() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(ScreenTip), new FrameworkPropertyMetadata(typeof(ScreenTip))); + StyleProperty.OverrideMetadata(typeof(ScreenTip), new FrameworkPropertyMetadata(null, OnCoerceStyle)); + } + + private static object OnCoerceStyle(DependencyObject d, object basevalue) + { + if (basevalue == null) + { + var frameworkElement = d as FrameworkElement; + if (frameworkElement != null) + { + basevalue = frameworkElement.TryFindResource(typeof(ScreenTip)); + } + } + + return basevalue; + } + + /// + /// Default constructor + /// + public ScreenTip() + { + this.Opened += this.OnToolTipOpened; + this.Closed += this.OnToolTipClosed; + this.CustomPopupPlacementCallback = this.CustomPopupPlacementMethod; + this.Placement = PlacementMode.Custom; + this.HelpLabelVisibility = Visibility.Visible; + } + + #endregion + + #region Popup Custom Placement + + // Calculate two variants: below and upper ribbon + CustomPopupPlacement[] CustomPopupPlacementMethod(Size popupSize, Size targetSize, Point offset) + { + if (this.PlacementTarget == null) + { + return new CustomPopupPlacement[] { }; + } + + Ribbon ribbon = null; + UIElement topLevelElement = null; + FindControls(this.PlacementTarget, ref ribbon, ref topLevelElement); + + // Exclude QAT items + var notQuickAccessItem = !IsQuickAccessItem(this.PlacementTarget); + var notContextMenuChild = !IsContextMenuChild(this.PlacementTarget); + var rightToLeftOffset = this.FlowDirection == FlowDirection.RightToLeft + ? -popupSize.Width + : 0; + + var decoratorChild = GetDecoratorChild(topLevelElement); + + if (notQuickAccessItem + && this.IsRibbonAligned + && ribbon != null) + { + var belowY = ribbon.TranslatePoint(new Point(0, ribbon.ActualHeight), this.PlacementTarget).Y; + var aboveY = ribbon.TranslatePoint(new Point(0, 0), this.PlacementTarget).Y - popupSize.Height; + var below = new CustomPopupPlacement(new Point(rightToLeftOffset, belowY + 1), PopupPrimaryAxis.Horizontal); + var above = new CustomPopupPlacement(new Point(rightToLeftOffset, aboveY - 1), PopupPrimaryAxis.Horizontal); + return new[] { below, above }; + } + + if (notQuickAccessItem + && this.IsRibbonAligned + && notContextMenuChild + && topLevelElement is Window == false + && decoratorChild != null) + { + // Placed on Popup? + var belowY = decoratorChild.TranslatePoint(new Point(0, ((FrameworkElement)decoratorChild).ActualHeight), this.PlacementTarget).Y; + var aboveY = decoratorChild.TranslatePoint(new Point(0, 0), this.PlacementTarget).Y - popupSize.Height; + var below = new CustomPopupPlacement(new Point(rightToLeftOffset, belowY + 1), PopupPrimaryAxis.Horizontal); + var above = new CustomPopupPlacement(new Point(rightToLeftOffset, aboveY - 1), PopupPrimaryAxis.Horizontal); + return new[] { below, above }; + } + + return new[] { + new CustomPopupPlacement(new Point(rightToLeftOffset, this.PlacementTarget.RenderSize.Height + 1), PopupPrimaryAxis.Horizontal), + new CustomPopupPlacement(new Point(rightToLeftOffset, -popupSize.Height - 1), PopupPrimaryAxis.Horizontal)}; + } + + private static bool IsContextMenuChild(UIElement element) + { + do + { + var parent = VisualTreeHelper.GetParent(element) as UIElement; + //if (parent is ContextMenuBar) return true; + element = parent; + } while (element != null); + + return false; + } + + private static bool IsQuickAccessItem(UIElement element) + { + do + { + var parent = VisualTreeHelper.GetParent(element) as UIElement; + if (parent is QuickAccessToolBar) + { + return true; + } + + element = parent; + } + while (element != null); + + return false; + } + + private static UIElement GetDecoratorChild(UIElement popupRoot) + { + if (popupRoot == null) + { + return null; + } + + var decorator = popupRoot as AdornerDecorator; + if (decorator != null) + { + return decorator.Child; + } + + for (var i = 0; i < VisualTreeHelper.GetChildrenCount(popupRoot); i++) + { + var element = GetDecoratorChild(VisualTreeHelper.GetChild(popupRoot, i) as UIElement); + if (element != null) + { + return element; + } + } + + return null; + } + + private static void FindControls(UIElement obj, ref Ribbon ribbon, ref UIElement topLevelElement) + { + if (obj == null) + { + return; + } + + var objRibbon = obj as Ribbon; + if (objRibbon != null) + { + ribbon = objRibbon; + } + + var parentVisual = VisualTreeHelper.GetParent(obj) as UIElement; + if (parentVisual == null) + { + topLevelElement = obj; + } + else + { + FindControls(parentVisual, ref ribbon, ref topLevelElement); + } + } + + #endregion + + #region Title Property + + /// + /// Gets or sets title of the screen tip + /// + [System.ComponentModel.DisplayName("Title"), + System.ComponentModel.Category("Screen Tip"), + System.ComponentModel.Description("Title of the screen tip")] + public string Title + { + get { return (string)this.GetValue(TitleProperty); } + set { this.SetValue(TitleProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Title. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty TitleProperty = + DependencyProperty.Register("Title", typeof(string), typeof(ScreenTip), new UIPropertyMetadata("")); + + #endregion + + #region Text Property + + /// + /// Gets or sets text of the screen tip + /// + [System.ComponentModel.DisplayName("Text"), + System.ComponentModel.Category("Screen Tip"), + System.ComponentModel.Description("Main text of the screen tip")] + public string Text + { + get { return (string)this.GetValue(TextProperty); } + set { this.SetValue(TextProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Text. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty TextProperty = + DependencyProperty.Register("Text", typeof(string), typeof(ScreenTip), new UIPropertyMetadata("")); + + #endregion + + #region DisableReason Property + + /// + /// Gets or sets disable reason of the associated screen tip's control + /// + [System.ComponentModel.DisplayName("Disable Reason"), + System.ComponentModel.Category("Screen Tip"), + System.ComponentModel.Description("Describe here what would cause disable of the control")] + public string DisableReason + { + get { return (string)this.GetValue(DisableReasonProperty); } + set { this.SetValue(DisableReasonProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for DisableReason. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty DisableReasonProperty = + DependencyProperty.Register("DisableReason", typeof(string), typeof(ScreenTip), new UIPropertyMetadata("")); + + #endregion + + #region HelpTopic Property + + /// + /// Gets or sets help topic of the ScreenTip + /// + [System.ComponentModel.DisplayName("Help Topic"), + System.ComponentModel.Category("Screen Tip"), + System.ComponentModel.Description("Help topic (it will be used to execute help)")] + public object HelpTopic + { + get { return (object)this.GetValue(HelpTopicProperty); } + set { this.SetValue(HelpTopicProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for HelpTopic. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty HelpTopicProperty = + DependencyProperty.Register("HelpTopic", typeof(object), typeof(ScreenTip), new UIPropertyMetadata(null)); + + #endregion + + #region Image Property + + /// + /// Gets or sets image of the screen tip + /// + [System.ComponentModel.DisplayName("Image"), + System.ComponentModel.Category("Screen Tip"), + System.ComponentModel.Description("Image of the screen tip")] + public ImageSource Image + { + get { return (ImageSource)this.GetValue(ImageProperty); } + set { this.SetValue(ImageProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Image. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty ImageProperty = + DependencyProperty.Register("Image", typeof(ImageSource), typeof(ScreenTip), new UIPropertyMetadata(null)); + + #endregion + + #region ShowHelp Property + /// + /// Shows or hides the Help Label + /// + [System.ComponentModel.DisplayName("HelpLabelVisibility"), + System.ComponentModel.Category("Screen Tip"), + System.ComponentModel.Description("Sets the visibility of the F1 Help Label")] + public Visibility HelpLabelVisibility + { + get { return (Visibility)this.GetValue(HelpLabelVisibilityProperty); } + set { this.SetValue(HelpLabelVisibilityProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store the boolean. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty HelpLabelVisibilityProperty = + DependencyProperty.Register("HelpLabelVisibility", typeof(Visibility), typeof(ScreenTip), new UIPropertyMetadata(Visibility.Visible)); + #endregion + + #region Help Invocation + + /// + /// Occurs when user press F1 on ScreenTip with HelpTopic filled + /// + public static event EventHandler HelpPressed; + + #endregion + + #region IsRibbonAligned + + /// + /// Gets or set whether ScreenTip should positioned below Ribbon + /// + public bool IsRibbonAligned + { + get { return (bool)this.GetValue(IsRibbonAlignedProperty); } + set { this.SetValue(IsRibbonAlignedProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for BelowRibbon. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IsRibbonAlignedProperty = + DependencyProperty.Register("BelowRibbon", typeof(bool), typeof(ScreenTip), + new UIPropertyMetadata(true)); + + + #endregion + + #region F1 Help Handling + + // Currently focused element + private IInputElement focusedElement; + + private void OnToolTipClosed(object sender, RoutedEventArgs e) + { + if (this.focusedElement == null) + { + return; + } + + this.focusedElement.PreviewKeyDown -= this.OnFocusedElementPreviewKeyDown; + this.focusedElement = null; + } + + private void OnToolTipOpened(object sender, RoutedEventArgs e) + { + if (this.HelpTopic == null) + { + return; + } + + this.focusedElement = Keyboard.FocusedElement; + if (this.focusedElement != null) + { + this.focusedElement.PreviewKeyDown += this.OnFocusedElementPreviewKeyDown; + } + } + + void OnFocusedElementPreviewKeyDown(object sender, KeyEventArgs e) + { + if (e.Key != Key.F1) + { + return; + } + + e.Handled = true; + + if (HelpPressed != null) + { + HelpPressed(null, new ScreenTipHelpEventArgs(this.HelpTopic)); + } + } + + #endregion + } + + /// + /// Event args for HelpPressed event handler + /// + public class ScreenTipHelpEventArgs : EventArgs + { + /// + /// Gets help topic associated with screen tip + /// + public object HelpTopic { get; private set; } + + /// + /// Constructor + /// + /// Help topic + public ScreenTipHelpEventArgs(object helpTopic) + { + this.HelpTopic = helpTopic; + } + } } \ No newline at end of file diff --git a/Fluent/Controls/SeparatorTabItem.cs b/Fluent.Ribbon/Controls/SeparatorTabItem.cs similarity index 97% rename from Fluent/Controls/SeparatorTabItem.cs rename to Fluent.Ribbon/Controls/SeparatorTabItem.cs index 2c3adbbe5..1e0251c92 100644 --- a/Fluent/Controls/SeparatorTabItem.cs +++ b/Fluent.Ribbon/Controls/SeparatorTabItem.cs @@ -1,61 +1,61 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Text; -using System.Windows; -using System.Windows.Controls; - -namespace Fluent -{ - /// - /// Represents separator to use in the TabControl - /// - public class SeparatorTabItem : TabItem - { - #region Constructors - - /// - /// Static constructor - /// - [SuppressMessage("Microsoft.Performance", "CA1810")] - static SeparatorTabItem() - { - Type type = typeof(SeparatorTabItem); - DefaultStyleKeyProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(type)); - IsEnabledProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(false, null, CoerceIsEnabledAndTabStop)); - IsTabStopProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(false, null, CoerceIsEnabledAndTabStop)); - IsSelectedProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(false, OnIsSelectedChanged)); - StyleProperty.OverrideMetadata(typeof(SeparatorTabItem), new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceStyle))); - } - - // Coerce object style - static object OnCoerceStyle(DependencyObject d, object basevalue) - { - if (basevalue == null) - { - basevalue = (d as FrameworkElement).TryFindResource(typeof(SeparatorTabItem)); - } - - return basevalue; - } - - static void OnIsSelectedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - if (!(bool)e.NewValue) return; - SeparatorTabItem separatorTabItem = (SeparatorTabItem)d; - TabControl tabControl = separatorTabItem.Parent as TabControl; - if (tabControl == null || tabControl.Items.Count <= 1) return; - tabControl.SelectedIndex = tabControl.SelectedIndex == tabControl.Items.Count - 1 - ? tabControl.SelectedIndex - 1 : - tabControl.SelectedIndex + 1; - } - - static object CoerceIsEnabledAndTabStop(DependencyObject d, object basevalue) - { - return false; - } - - #endregion - } -} +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; + +namespace Fluent +{ + /// + /// Represents separator to use in the TabControl + /// + public class SeparatorTabItem : TabItem + { + #region Constructors + + /// + /// Static constructor + /// + [SuppressMessage("Microsoft.Performance", "CA1810")] + static SeparatorTabItem() + { + Type type = typeof(SeparatorTabItem); + DefaultStyleKeyProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(type)); + IsEnabledProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(false, null, CoerceIsEnabledAndTabStop)); + IsTabStopProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(false, null, CoerceIsEnabledAndTabStop)); + IsSelectedProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(false, OnIsSelectedChanged)); + StyleProperty.OverrideMetadata(typeof(SeparatorTabItem), new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceStyle))); + } + + // Coerce object style + static object OnCoerceStyle(DependencyObject d, object basevalue) + { + if (basevalue == null) + { + basevalue = (d as FrameworkElement).TryFindResource(typeof(SeparatorTabItem)); + } + + return basevalue; + } + + static void OnIsSelectedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (!(bool)e.NewValue) return; + SeparatorTabItem separatorTabItem = (SeparatorTabItem)d; + TabControl tabControl = separatorTabItem.Parent as TabControl; + if (tabControl == null || tabControl.Items.Count <= 1) return; + tabControl.SelectedIndex = tabControl.SelectedIndex == tabControl.Items.Count - 1 + ? tabControl.SelectedIndex - 1 : + tabControl.SelectedIndex + 1; + } + + static object CoerceIsEnabledAndTabStop(DependencyObject d, object basevalue) + { + return false; + } + + #endregion + } +} diff --git a/Fluent/Controls/Spinner.cs b/Fluent.Ribbon/Controls/Spinner.cs similarity index 97% rename from Fluent/Controls/Spinner.cs rename to Fluent.Ribbon/Controls/Spinner.cs index ae39a6f12..e2f35f514 100644 --- a/Fluent/Controls/Spinner.cs +++ b/Fluent.Ribbon/Controls/Spinner.cs @@ -1,552 +1,552 @@ -namespace Fluent -{ - using System; - using System.Diagnostics; - using System.Diagnostics.CodeAnalysis; - using System.Globalization; - using System.Text; - using System.Threading; - using System.Windows; - using System.Windows.Controls.Primitives; - using System.Windows.Data; - using System.Windows.Input; - using System.Windows.Markup; - using System.Windows.Threading; - using Fluent.Internal; - - /// - /// Represents spinner control - /// - [ContentProperty("Value")] - [TemplatePart(Name = "PART_TextBox", Type = typeof(System.Windows.Controls.TextBox))] - [TemplatePart(Name = "PART_ButtonUp", Type = typeof(RepeatButton))] - [TemplatePart(Name = "PART_ButtonDown", Type = typeof(RepeatButton))] - public class Spinner : RibbonControl - { - #region Events - - /// - /// Occurs when value has been changed - /// - public event RoutedPropertyChangedEventHandler ValueChanged; - - #endregion - - #region Fields - - // Parts of the control (must be in control template) - private System.Windows.Controls.TextBox textBox; - private RepeatButton buttonUp; - private RepeatButton buttonDown; - - #endregion - - #region Properties - - #region Value - - /// - /// Gets or sets current value - /// - [SuppressMessage("Microsoft.Naming", "CA1721")] - public double Value - { - get { return (double)this.GetValue(ValueProperty); } - set { this.SetValue(ValueProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Value. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty ValueProperty; - - private static object CoerceValue(DependencyObject d, object basevalue) - { - var spinner = (Spinner)d; - var value = (double)basevalue; - value = GetLimitedValue(spinner, value); - return value; - } - - private static double GetLimitedValue(Spinner spinner, double value) - { - value = Math.Max(spinner.Minimum, value); - value = Math.Min(spinner.Maximum, value); - return value; - } - - private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var spinner = (Spinner)d; - spinner.ValueToTextBoxText(); - - if (spinner.ValueChanged != null) - { - spinner.ValueChanged(spinner, new RoutedPropertyChangedEventArgs((double)e.OldValue, (double)e.NewValue)); - } - } - - private void ValueToTextBoxText() - { - if (this.IsTemplateValid()) - { - this.textBox.Text = this.Value.ToString(this.Format, CultureInfo.CurrentCulture); - this.Text = this.textBox.Text; - } - } - - #endregion - - #region Text - - /// - /// Gets current text from the spinner - /// - public string Text - { - get { return (string)this.GetValue(TextProperty); } - private set { this.SetValue(textPropertyKey, value); } - } - - private static readonly DependencyPropertyKey textPropertyKey = DependencyProperty.RegisterReadOnly("Text", typeof(string), typeof(Spinner), new UIPropertyMetadata(null)); - - /// - /// Using a DependencyProperty as the backing store for Text. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty TextProperty = textPropertyKey.DependencyProperty; - - #endregion - - #region Increment - - /// - /// Gets or sets a value added or subtracted from the value property - /// - public double Increment - { - get { return (double)this.GetValue(IncrementProperty); } - set { this.SetValue(IncrementProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Increment. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IncrementProperty = - DependencyProperty.Register("Increment", typeof(double), typeof(Spinner), new UIPropertyMetadata(1.0d)); - - #endregion - - #region Minimum - - /// - /// Gets or sets minimun value - /// - public double Minimum - { - get { return (double)this.GetValue(MinimumProperty); } - set { this.SetValue(MinimumProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Minimum. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty MinimumProperty; - - static object CoerceMinimum(DependencyObject d, object basevalue) - { - var spinner = (Spinner)d; - var value = (double)basevalue; - - if (spinner.Maximum < value) - { - return spinner.Maximum; - } - - return value; - } - - static void OnMinimumChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var spinner = (Spinner)d; - var value = (double)CoerceValue(d, spinner.Value); - - if (DoubleUtil.AreClose(value, spinner.Value) == false) - { - spinner.Value = value; - } - } - - #endregion - - #region Maximum - - /// - /// Gets or sets maximum value - /// - public double Maximum - { - get { return (double)this.GetValue(MaximumProperty); } - set { this.SetValue(MaximumProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Maximum. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty MaximumProperty; - - static object CoerceMaximum(DependencyObject d, object basevalue) - { - var spinner = (Spinner)d; - var value = (double)basevalue; - - if (spinner.Minimum > value) - { - return spinner.Minimum; - } - - return value; - } - - static void OnMaximumChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var spinner = (Spinner)d; - var value = (double)CoerceValue(d, spinner.Value); - - if (DoubleUtil.AreClose(value, spinner.Value) == false) - { - spinner.Value = value; - } - } - - #endregion - - #region Format - - /// - /// Gets or sets string format of value - /// - public string Format - { - get { return (string)this.GetValue(FormatProperty); } - set { this.SetValue(FormatProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Format. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty FormatProperty = - DependencyProperty.Register("Format", typeof(string), typeof(Spinner), new UIPropertyMetadata("F1", OnFormatChanged)); - - static void OnFormatChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var spinner = (Spinner)d; - spinner.ValueToTextBoxText(); - } - - #endregion - - #region Delay - - /// - /// Gets or sets the amount of time, in milliseconds, - /// the Spinner waits while it is pressed before it starts repeating. - /// The value must be non-negative. This is a dependency property. - /// - public int Delay - { - get { return (int)this.GetValue(DelayProperty); } - set { this.SetValue(DelayProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Delay. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty DelayProperty = - DependencyProperty.Register("Delay", typeof(int), typeof(Spinner), - new UIPropertyMetadata(400)); - - #endregion - - #region Interval - - /// - /// Gets or sets the amount of time, in milliseconds, - /// between repeats once repeating starts. The value must be non-negative. - /// This is a dependency property. - /// - public int Interval - { - get { return (int)this.GetValue(IntervalProperty); } - set { this.SetValue(IntervalProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Interval. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IntervalProperty = - DependencyProperty.Register("Interval", typeof(int), typeof(Spinner), new UIPropertyMetadata(80)); - - #endregion - - #region InputWidth - - /// - /// Gets or sets width of the value input part of spinner - /// - public double InputWidth - { - get { return (double)this.GetValue(InputWidthProperty); } - set { this.SetValue(InputWidthProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for InputWidth. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty InputWidthProperty = - DependencyProperty.Register("InputWidth", typeof(double), typeof(Spinner), new UIPropertyMetadata(double.NaN)); - - #endregion - - #endregion - - #region Constructors - - /// - /// Static constructor - /// - [SuppressMessage("Microsoft.Performance", "CA1810")] - static Spinner() - { - DefaultStyleKeyProperty.OverrideMetadata(typeof(Spinner), new FrameworkPropertyMetadata(typeof(Spinner))); - - MaximumProperty = DependencyProperty.Register("Maximum", typeof(double), typeof(Spinner), new UIPropertyMetadata(double.MaxValue, OnMaximumChanged, CoerceMaximum)); - MinimumProperty = DependencyProperty.Register("Minimum", typeof(double), typeof(Spinner), new UIPropertyMetadata(0.0d, OnMinimumChanged, CoerceMinimum)); - ValueProperty = DependencyProperty.Register("Value", typeof(double), typeof(Spinner), new FrameworkPropertyMetadata(0.0d, OnValueChanged, CoerceValue) { BindsTwoWayByDefault = true }); - - KeyboardNavigation.TabNavigationProperty.OverrideMetadata(typeof(Spinner), new FrameworkPropertyMetadata(KeyboardNavigationMode.Once)); - } - - #endregion - - #region Overrides - - /// - /// When overridden in a derived class, is invoked whenever application code or internal processes call . - /// - public override void OnApplyTemplate() - { - if (this.IsTemplateValid()) - { - this.buttonUp.Click -= this.OnButtonUpClick; - this.buttonDown.Click -= this.OnButtonDownClick; - BindingOperations.ClearAllBindings(this.buttonDown); - BindingOperations.ClearAllBindings(this.buttonUp); - } - - // Get template childs - this.textBox = this.GetTemplateChild("PART_TextBox") as System.Windows.Controls.TextBox; - this.buttonUp = this.GetTemplateChild("PART_ButtonUp") as RepeatButton; - this.buttonDown = this.GetTemplateChild("PART_ButtonDown") as RepeatButton; - - // Check template - if (this.IsTemplateValid() == false) - { - Debug.WriteLine("Template for Spinner control is invalid"); - return; - } - - // Bindings - Bind(this, this.buttonUp, "Delay", RepeatButton.DelayProperty, BindingMode.OneWay); - Bind(this, this.buttonDown, "Delay", RepeatButton.DelayProperty, BindingMode.OneWay); - Bind(this, this.buttonUp, "Interval", RepeatButton.IntervalProperty, BindingMode.OneWay); - Bind(this, this.buttonDown, "Interval", RepeatButton.IntervalProperty, BindingMode.OneWay); - - - // Events subscribing - this.buttonUp.Click += this.OnButtonUpClick; - this.buttonDown.Click += this.OnButtonDownClick; - this.textBox.LostKeyboardFocus += this.OnTextBoxLostKeyboardFocus; - this.textBox.PreviewKeyDown += this.OnTextBoxPreviewKeyDown; - - this.ValueToTextBoxText(); - } - - bool IsTemplateValid() - { - return this.textBox != null - && this.buttonUp != null - && this.buttonDown != null; - } - - #endregion - - #region Event Handling - - /// - /// Handles key tip pressed - /// - public override void OnKeyTipPressed() - { - if (this.IsTemplateValid() == false) - { - return; - } - - // Use dispatcher to avoid focus moving to backup'ed element - // (focused element before keytips processing) - this.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, - (ThreadStart)(() => - { - this.textBox.SelectAll(); - this.textBox.Focus(); - })); - base.OnKeyTipPressed(); - } - - /// - /// Invoked when an unhandled System.Windows.Input.Keyboard.KeyUp�attached event reaches - /// an element in its route that is derived from this class. Implement this method to add class handling for this event. - /// - /// The System.Windows.Input.KeyEventArgs that contains the event data. - protected override void OnKeyUp(KeyEventArgs e) - { - // Avoid Click invocation (from RibbonControl) - if (e.Key == Key.Enter - || e.Key == Key.Space) - { - return; - } - - base.OnKeyUp(e); - } - - private void OnButtonUpClick(object sender, RoutedEventArgs e) - { - this.Value = GetLimitedValue(this, this.Value + this.Increment); - } - - private void OnButtonDownClick(object sender, RoutedEventArgs e) - { - this.Value = GetLimitedValue(this, this.Value - this.Increment); - } - - private void OnTextBoxLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e) - { - this.TextBoxTextToValue(); - } - - private void OnTextBoxPreviewKeyDown(object sender, KeyEventArgs e) - { - if (e.Key == Key.Enter) - { - this.TextBoxTextToValue(); - } - - if (e.Key == Key.Escape) - { - this.ValueToTextBoxText(); - } - - if (e.Key == Key.Enter - || e.Key == Key.Escape) - { - // Move Focus - this.textBox.Focusable = false; - this.Focus(); - this.textBox.Focusable = true; - e.Handled = true; - } - - if (e.Key == Key.Up) - { - this.buttonUp.RaiseEvent(new RoutedEventArgs(ButtonBase.ClickEvent)); - } - - if (e.Key == Key.Down) - { - this.buttonDown.RaiseEvent(new RoutedEventArgs(ButtonBase.ClickEvent)); - } - } - - private void TextBoxTextToValue() - { - var text = this.textBox.Text; - - // Remove all except digits, signs and commas - var stringBuilder = new StringBuilder(); - - foreach (var symbol in text) - { - if (char.IsDigit(symbol) - || symbol == ',' - || symbol == '.' - || (symbol == '-' && stringBuilder.Length == 0)) - { - stringBuilder.Append(symbol); - } - } - - text = stringBuilder.ToString(); - - double value; - - if (double.TryParse(text, NumberStyles.Any, CultureInfo.CurrentCulture, out value)) - { - this.Value = GetLimitedValue(this, value); - } - - this.ValueToTextBoxText(); - } - - #endregion - - #region Quick Access Item Creating - - /// - /// Gets control which represents shortcut item. - /// This item MUST be syncronized with the original - /// and send command to original one control. - /// - /// Control which represents shortcut item - public override FrameworkElement CreateQuickAccessItem() - { - var spinner = new Spinner(); - this.BindQuickAccessItem(spinner); - return spinner; - } - - /// - /// This method must be overriden to bind properties to use in quick access creating - /// - /// Toolbar item - protected void BindQuickAccessItem(FrameworkElement element) - { - var spinner = (Spinner)element; - - BindQuickAccessItem(this, element); - - spinner.Width = this.Width; - spinner.InputWidth = this.InputWidth; - - Bind(this, spinner, "Value", ValueProperty, BindingMode.TwoWay); - Bind(this, spinner, "Increment", IncrementProperty, BindingMode.OneWay); - Bind(this, spinner, "Minimum", MinimumProperty, BindingMode.OneWay); - Bind(this, spinner, "Maximum", MaximumProperty, BindingMode.OneWay); - Bind(this, spinner, "Format", FormatProperty, BindingMode.OneWay); - Bind(this, spinner, "Delay", DelayProperty, BindingMode.OneWay); - Bind(this, spinner, "Interval", IntervalProperty, BindingMode.OneWay); - - BindQuickAccessItem(this, element); - } - - #endregion - } +namespace Fluent +{ + using System; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Globalization; + using System.Text; + using System.Threading; + using System.Windows; + using System.Windows.Controls.Primitives; + using System.Windows.Data; + using System.Windows.Input; + using System.Windows.Markup; + using System.Windows.Threading; + using Fluent.Internal; + + /// + /// Represents spinner control + /// + [ContentProperty("Value")] + [TemplatePart(Name = "PART_TextBox", Type = typeof(System.Windows.Controls.TextBox))] + [TemplatePart(Name = "PART_ButtonUp", Type = typeof(RepeatButton))] + [TemplatePart(Name = "PART_ButtonDown", Type = typeof(RepeatButton))] + public class Spinner : RibbonControl + { + #region Events + + /// + /// Occurs when value has been changed + /// + public event RoutedPropertyChangedEventHandler ValueChanged; + + #endregion + + #region Fields + + // Parts of the control (must be in control template) + private System.Windows.Controls.TextBox textBox; + private RepeatButton buttonUp; + private RepeatButton buttonDown; + + #endregion + + #region Properties + + #region Value + + /// + /// Gets or sets current value + /// + [SuppressMessage("Microsoft.Naming", "CA1721")] + public double Value + { + get { return (double)this.GetValue(ValueProperty); } + set { this.SetValue(ValueProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Value. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty ValueProperty; + + private static object CoerceValue(DependencyObject d, object basevalue) + { + var spinner = (Spinner)d; + var value = (double)basevalue; + value = GetLimitedValue(spinner, value); + return value; + } + + private static double GetLimitedValue(Spinner spinner, double value) + { + value = Math.Max(spinner.Minimum, value); + value = Math.Min(spinner.Maximum, value); + return value; + } + + private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var spinner = (Spinner)d; + spinner.ValueToTextBoxText(); + + if (spinner.ValueChanged != null) + { + spinner.ValueChanged(spinner, new RoutedPropertyChangedEventArgs((double)e.OldValue, (double)e.NewValue)); + } + } + + private void ValueToTextBoxText() + { + if (this.IsTemplateValid()) + { + this.textBox.Text = this.Value.ToString(this.Format, CultureInfo.CurrentCulture); + this.Text = this.textBox.Text; + } + } + + #endregion + + #region Text + + /// + /// Gets current text from the spinner + /// + public string Text + { + get { return (string)this.GetValue(TextProperty); } + private set { this.SetValue(textPropertyKey, value); } + } + + private static readonly DependencyPropertyKey textPropertyKey = DependencyProperty.RegisterReadOnly("Text", typeof(string), typeof(Spinner), new UIPropertyMetadata(null)); + + /// + /// Using a DependencyProperty as the backing store for Text. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty TextProperty = textPropertyKey.DependencyProperty; + + #endregion + + #region Increment + + /// + /// Gets or sets a value added or subtracted from the value property + /// + public double Increment + { + get { return (double)this.GetValue(IncrementProperty); } + set { this.SetValue(IncrementProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Increment. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IncrementProperty = + DependencyProperty.Register("Increment", typeof(double), typeof(Spinner), new UIPropertyMetadata(1.0d)); + + #endregion + + #region Minimum + + /// + /// Gets or sets minimun value + /// + public double Minimum + { + get { return (double)this.GetValue(MinimumProperty); } + set { this.SetValue(MinimumProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Minimum. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty MinimumProperty; + + static object CoerceMinimum(DependencyObject d, object basevalue) + { + var spinner = (Spinner)d; + var value = (double)basevalue; + + if (spinner.Maximum < value) + { + return spinner.Maximum; + } + + return value; + } + + static void OnMinimumChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var spinner = (Spinner)d; + var value = (double)CoerceValue(d, spinner.Value); + + if (DoubleUtil.AreClose(value, spinner.Value) == false) + { + spinner.Value = value; + } + } + + #endregion + + #region Maximum + + /// + /// Gets or sets maximum value + /// + public double Maximum + { + get { return (double)this.GetValue(MaximumProperty); } + set { this.SetValue(MaximumProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Maximum. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty MaximumProperty; + + static object CoerceMaximum(DependencyObject d, object basevalue) + { + var spinner = (Spinner)d; + var value = (double)basevalue; + + if (spinner.Minimum > value) + { + return spinner.Minimum; + } + + return value; + } + + static void OnMaximumChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var spinner = (Spinner)d; + var value = (double)CoerceValue(d, spinner.Value); + + if (DoubleUtil.AreClose(value, spinner.Value) == false) + { + spinner.Value = value; + } + } + + #endregion + + #region Format + + /// + /// Gets or sets string format of value + /// + public string Format + { + get { return (string)this.GetValue(FormatProperty); } + set { this.SetValue(FormatProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Format. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty FormatProperty = + DependencyProperty.Register("Format", typeof(string), typeof(Spinner), new UIPropertyMetadata("F1", OnFormatChanged)); + + static void OnFormatChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var spinner = (Spinner)d; + spinner.ValueToTextBoxText(); + } + + #endregion + + #region Delay + + /// + /// Gets or sets the amount of time, in milliseconds, + /// the Spinner waits while it is pressed before it starts repeating. + /// The value must be non-negative. This is a dependency property. + /// + public int Delay + { + get { return (int)this.GetValue(DelayProperty); } + set { this.SetValue(DelayProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Delay. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty DelayProperty = + DependencyProperty.Register("Delay", typeof(int), typeof(Spinner), + new UIPropertyMetadata(400)); + + #endregion + + #region Interval + + /// + /// Gets or sets the amount of time, in milliseconds, + /// between repeats once repeating starts. The value must be non-negative. + /// This is a dependency property. + /// + public int Interval + { + get { return (int)this.GetValue(IntervalProperty); } + set { this.SetValue(IntervalProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Interval. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IntervalProperty = + DependencyProperty.Register("Interval", typeof(int), typeof(Spinner), new UIPropertyMetadata(80)); + + #endregion + + #region InputWidth + + /// + /// Gets or sets width of the value input part of spinner + /// + public double InputWidth + { + get { return (double)this.GetValue(InputWidthProperty); } + set { this.SetValue(InputWidthProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for InputWidth. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty InputWidthProperty = + DependencyProperty.Register("InputWidth", typeof(double), typeof(Spinner), new UIPropertyMetadata(double.NaN)); + + #endregion + + #endregion + + #region Constructors + + /// + /// Static constructor + /// + [SuppressMessage("Microsoft.Performance", "CA1810")] + static Spinner() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(Spinner), new FrameworkPropertyMetadata(typeof(Spinner))); + + MaximumProperty = DependencyProperty.Register("Maximum", typeof(double), typeof(Spinner), new UIPropertyMetadata(double.MaxValue, OnMaximumChanged, CoerceMaximum)); + MinimumProperty = DependencyProperty.Register("Minimum", typeof(double), typeof(Spinner), new UIPropertyMetadata(0.0d, OnMinimumChanged, CoerceMinimum)); + ValueProperty = DependencyProperty.Register("Value", typeof(double), typeof(Spinner), new FrameworkPropertyMetadata(0.0d, OnValueChanged, CoerceValue) { BindsTwoWayByDefault = true }); + + KeyboardNavigation.TabNavigationProperty.OverrideMetadata(typeof(Spinner), new FrameworkPropertyMetadata(KeyboardNavigationMode.Once)); + } + + #endregion + + #region Overrides + + /// + /// When overridden in a derived class, is invoked whenever application code or internal processes call . + /// + public override void OnApplyTemplate() + { + if (this.IsTemplateValid()) + { + this.buttonUp.Click -= this.OnButtonUpClick; + this.buttonDown.Click -= this.OnButtonDownClick; + BindingOperations.ClearAllBindings(this.buttonDown); + BindingOperations.ClearAllBindings(this.buttonUp); + } + + // Get template childs + this.textBox = this.GetTemplateChild("PART_TextBox") as System.Windows.Controls.TextBox; + this.buttonUp = this.GetTemplateChild("PART_ButtonUp") as RepeatButton; + this.buttonDown = this.GetTemplateChild("PART_ButtonDown") as RepeatButton; + + // Check template + if (this.IsTemplateValid() == false) + { + Debug.WriteLine("Template for Spinner control is invalid"); + return; + } + + // Bindings + Bind(this, this.buttonUp, "Delay", RepeatButton.DelayProperty, BindingMode.OneWay); + Bind(this, this.buttonDown, "Delay", RepeatButton.DelayProperty, BindingMode.OneWay); + Bind(this, this.buttonUp, "Interval", RepeatButton.IntervalProperty, BindingMode.OneWay); + Bind(this, this.buttonDown, "Interval", RepeatButton.IntervalProperty, BindingMode.OneWay); + + + // Events subscribing + this.buttonUp.Click += this.OnButtonUpClick; + this.buttonDown.Click += this.OnButtonDownClick; + this.textBox.LostKeyboardFocus += this.OnTextBoxLostKeyboardFocus; + this.textBox.PreviewKeyDown += this.OnTextBoxPreviewKeyDown; + + this.ValueToTextBoxText(); + } + + bool IsTemplateValid() + { + return this.textBox != null + && this.buttonUp != null + && this.buttonDown != null; + } + + #endregion + + #region Event Handling + + /// + /// Handles key tip pressed + /// + public override void OnKeyTipPressed() + { + if (this.IsTemplateValid() == false) + { + return; + } + + // Use dispatcher to avoid focus moving to backup'ed element + // (focused element before keytips processing) + this.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, + (ThreadStart)(() => + { + this.textBox.SelectAll(); + this.textBox.Focus(); + })); + base.OnKeyTipPressed(); + } + + /// + /// Invoked when an unhandled System.Windows.Input.Keyboard.KeyUp�attached event reaches + /// an element in its route that is derived from this class. Implement this method to add class handling for this event. + /// + /// The System.Windows.Input.KeyEventArgs that contains the event data. + protected override void OnKeyUp(KeyEventArgs e) + { + // Avoid Click invocation (from RibbonControl) + if (e.Key == Key.Enter + || e.Key == Key.Space) + { + return; + } + + base.OnKeyUp(e); + } + + private void OnButtonUpClick(object sender, RoutedEventArgs e) + { + this.Value = GetLimitedValue(this, this.Value + this.Increment); + } + + private void OnButtonDownClick(object sender, RoutedEventArgs e) + { + this.Value = GetLimitedValue(this, this.Value - this.Increment); + } + + private void OnTextBoxLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e) + { + this.TextBoxTextToValue(); + } + + private void OnTextBoxPreviewKeyDown(object sender, KeyEventArgs e) + { + if (e.Key == Key.Enter) + { + this.TextBoxTextToValue(); + } + + if (e.Key == Key.Escape) + { + this.ValueToTextBoxText(); + } + + if (e.Key == Key.Enter + || e.Key == Key.Escape) + { + // Move Focus + this.textBox.Focusable = false; + this.Focus(); + this.textBox.Focusable = true; + e.Handled = true; + } + + if (e.Key == Key.Up) + { + this.buttonUp.RaiseEvent(new RoutedEventArgs(ButtonBase.ClickEvent)); + } + + if (e.Key == Key.Down) + { + this.buttonDown.RaiseEvent(new RoutedEventArgs(ButtonBase.ClickEvent)); + } + } + + private void TextBoxTextToValue() + { + var text = this.textBox.Text; + + // Remove all except digits, signs and commas + var stringBuilder = new StringBuilder(); + + foreach (var symbol in text) + { + if (char.IsDigit(symbol) + || symbol == ',' + || symbol == '.' + || (symbol == '-' && stringBuilder.Length == 0)) + { + stringBuilder.Append(symbol); + } + } + + text = stringBuilder.ToString(); + + double value; + + if (double.TryParse(text, NumberStyles.Any, CultureInfo.CurrentCulture, out value)) + { + this.Value = GetLimitedValue(this, value); + } + + this.ValueToTextBoxText(); + } + + #endregion + + #region Quick Access Item Creating + + /// + /// Gets control which represents shortcut item. + /// This item MUST be syncronized with the original + /// and send command to original one control. + /// + /// Control which represents shortcut item + public override FrameworkElement CreateQuickAccessItem() + { + var spinner = new Spinner(); + this.BindQuickAccessItem(spinner); + return spinner; + } + + /// + /// This method must be overriden to bind properties to use in quick access creating + /// + /// Toolbar item + protected void BindQuickAccessItem(FrameworkElement element) + { + var spinner = (Spinner)element; + + BindQuickAccessItem(this, element); + + spinner.Width = this.Width; + spinner.InputWidth = this.InputWidth; + + Bind(this, spinner, "Value", ValueProperty, BindingMode.TwoWay); + Bind(this, spinner, "Increment", IncrementProperty, BindingMode.OneWay); + Bind(this, spinner, "Minimum", MinimumProperty, BindingMode.OneWay); + Bind(this, spinner, "Maximum", MaximumProperty, BindingMode.OneWay); + Bind(this, spinner, "Format", FormatProperty, BindingMode.OneWay); + Bind(this, spinner, "Delay", DelayProperty, BindingMode.OneWay); + Bind(this, spinner, "Interval", IntervalProperty, BindingMode.OneWay); + + BindQuickAccessItem(this, element); + } + + #endregion + } } \ No newline at end of file diff --git a/Fluent/Controls/SplitButton.cs b/Fluent.Ribbon/Controls/SplitButton.cs similarity index 97% rename from Fluent/Controls/SplitButton.cs rename to Fluent.Ribbon/Controls/SplitButton.cs index c9e87da49..7ea29845e 100644 --- a/Fluent/Controls/SplitButton.cs +++ b/Fluent.Ribbon/Controls/SplitButton.cs @@ -1,521 +1,521 @@ -using System.Collections; -using System.ComponentModel; -using System.Diagnostics.CodeAnalysis; -using System.Windows; -using System.Windows.Controls.Primitives; -using System.Windows.Data; -using System.Windows.Input; - -namespace Fluent -{ - /// - /// Represents button control that allows - /// you to add menu and handle clicks - /// - [TemplatePart(Name = "PART_Button", Type = typeof(ButtonBase))] - public class SplitButton : DropDownButton, IToggleButton, ICommandSource - { - #region Fields - - // Inner button - ToggleButton button; - - #endregion - - #region Properties - - /// - /// Gets an enumerator for logical child elements of this element. - /// - protected override IEnumerator LogicalChildren - { - get - { - var baseEnumerator = base.LogicalChildren; - while (baseEnumerator.MoveNext()) - { - yield return baseEnumerator.Current; - } - - if (this.button != null) - { - yield return this.button; - } - } - } - - #region Command - - /// - /// Gets or sets the command to invoke when this button is pressed. This is a dependency property. - /// - [Category("Action"), Localizability(LocalizationCategory.NeverLocalize), Bindable(true)] - public ICommand Command - { - get - { - return (ICommand)this.GetValue(CommandProperty); - } - set - { - this.SetValue(CommandProperty, value); - } - } - - /// - /// Gets or sets the parameter to pass to the System.Windows.Controls.Primitives.ButtonBase.Command property. This is a dependency property. - /// - [Bindable(true), Localizability(LocalizationCategory.NeverLocalize), Category("Action")] - public object CommandParameter - { - get - { - return this.GetValue(CommandParameterProperty); - } - set - { - this.SetValue(CommandParameterProperty, value); - } - } - - /// - /// Gets or sets the element on which to raise the specified command. This is a dependency property. - /// - [Bindable(true), Category("Action")] - public IInputElement CommandTarget - { - get - { - return (IInputElement)this.GetValue(CommandTargetProperty); - } - set - { - this.SetValue(CommandTargetProperty, value); - } - } - - /// - /// Identifies the CommandParameter dependency property. - /// - public static readonly DependencyProperty CommandParameterProperty = ButtonBase.CommandParameterProperty.AddOwner(typeof(SplitButton), new FrameworkPropertyMetadata(null)); - /// - /// Identifies the routed Command dependency property. - /// - public static readonly DependencyProperty CommandProperty = ButtonBase.CommandProperty.AddOwner(typeof(SplitButton), new FrameworkPropertyMetadata(null)); - - /// - /// Identifies the CommandTarget dependency property. - /// - public static readonly DependencyProperty CommandTargetProperty = ButtonBase.CommandTargetProperty.AddOwner(typeof(SplitButton), new FrameworkPropertyMetadata(null)); - - #endregion - - #region GroupName - - /// - /// Gets or sets the name of the group that the toggle button belongs to. - /// Use the GroupName property to specify a grouping of toggle buttons to - /// create a mutually exclusive set of controls. You can use the GroupName - /// property when only one selection is possible from a list of available - /// options. When this property is set, only one ToggleButton in the specified - /// group can be selected at a time. - /// - public string GroupName - { - get { return (string)this.GetValue(GroupNameProperty); } - set { this.SetValue(GroupNameProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for GroupName. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty GroupNameProperty = - DependencyProperty.Register("GroupName", typeof(string), typeof(SplitButton), - new UIPropertyMetadata(null, ToggleButtonHelper.OnGroupNameChanged)); - - #endregion - - #region IsChecked - - /// - /// Gets or sets a value indicating whether SplitButton is checked - /// - public bool? IsChecked - { - get { return (bool)this.GetValue(IsCheckedProperty); } - set { this.SetValue(IsCheckedProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for IsChecked. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IsCheckedProperty = - DependencyProperty.Register("IsChecked", typeof(bool?), typeof(SplitButton), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnIsCheckedChanged, CoerceIsChecked)); - - private static void OnIsCheckedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - SplitButton button = d as SplitButton; - if (button.IsCheckable) - { - if ((bool)e.NewValue) button.RaiseEvent(new RoutedEventArgs(CheckedEvent, button)); - else button.RaiseEvent(new RoutedEventArgs(UncheckedEvent, button)); - - ToggleButtonHelper.OnIsCheckedChanged(d, e); - } - } - - private static object CoerceIsChecked(DependencyObject d, object basevalue) - { - SplitButton button = d as SplitButton; - - if (!button.IsCheckable) return false; - - return ToggleButtonHelper.CoerceIsChecked(d, basevalue); - } - - #endregion - - #region IsCheckable - - /// - /// Gets or sets a value indicating whether SplitButton can be checked - /// - public bool IsCheckable - { - get { return (bool)this.GetValue(IsCheckableProperty); } - set { this.SetValue(IsCheckableProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for IsChecked. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IsCheckableProperty = - DependencyProperty.Register("IsCheckable", typeof(bool), typeof(SplitButton), new UIPropertyMetadata(false)); - - #endregion - - #region DropDownToolTip - - /// - /// Gets or sets tooltip of dropdown part of split button - /// - public object DropDownToolTip - { - get { return this.GetValue(DropDownToolTipProperty); } - set { this.SetValue(DropDownToolTipProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for DropDownToolTip. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty DropDownToolTipProperty = - DependencyProperty.Register("DropDownToolTip", typeof(object), typeof(SplitButton), new UIPropertyMetadata(null)); - - #endregion - - #region IsButtonEnabled - - /// - /// Gets or sets a value indicating whether dropdown part of split button is enabled - /// - public bool IsButtonEnabled - { - get { return (bool)this.GetValue(IsButtonEnabledProperty); } - set { this.SetValue(IsButtonEnabledProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for IsDropDownEnabled. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IsButtonEnabledProperty = - DependencyProperty.Register("IsButtonEnabled", typeof(bool), typeof(SplitButton), new UIPropertyMetadata(true)); - - #endregion - - #region IsDefinitive - - /// - /// Gets or sets whether ribbon control click must close backstage - /// - public bool IsDefinitive - { - get { return (bool)this.GetValue(IsDefinitiveProperty); } - set { this.SetValue(IsDefinitiveProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for IsDefinitive. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IsDefinitiveProperty = - DependencyProperty.Register("IsDefinitive", typeof(bool), typeof(SplitButton), new UIPropertyMetadata(true)); - - #endregion - - #endregion - - #region Events - - /// - /// Occurs when user clicks - /// - public static readonly RoutedEvent ClickEvent = ButtonBase.ClickEvent.AddOwner(typeof(SplitButton)); - - /// - /// Occurs when user clicks - /// - public event RoutedEventHandler Click - { - add - { - this.AddHandler(ClickEvent, value); - } - - remove - { - this.RemoveHandler(ClickEvent, value); - } - } - - /// - /// Occurs when button is checked - /// - public static readonly RoutedEvent CheckedEvent = System.Windows.Controls.Primitives.ToggleButton.CheckedEvent.AddOwner(typeof(SplitButton)); - - /// - /// Occurs when button is checked - /// - public event RoutedEventHandler Checked - { - add - { - this.AddHandler(CheckedEvent, value); - } - - remove - { - this.RemoveHandler(CheckedEvent, value); - } - } - - /// - /// Occurs when button is unchecked - /// - public static readonly RoutedEvent UncheckedEvent = System.Windows.Controls.Primitives.ToggleButton.UncheckedEvent.AddOwner(typeof(SplitButton)); - - /// - /// Occurs when button is unchecked - /// - public event RoutedEventHandler Unchecked - { - add - { - this.AddHandler(UncheckedEvent, value); - } - - remove - { - this.RemoveHandler(UncheckedEvent, value); - } - } - - #endregion - - #region Constructors - - [SuppressMessage("Microsoft.Performance", "CA1810")] - static SplitButton() - { - DefaultStyleKeyProperty.OverrideMetadata(typeof(SplitButton), new FrameworkPropertyMetadata(typeof(SplitButton))); - FocusVisualStyleProperty.OverrideMetadata(typeof(SplitButton), new FrameworkPropertyMetadata(null)); - StyleProperty.OverrideMetadata(typeof(SplitButton), new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceStyle))); - } - - // Coerce object style - static object OnCoerceStyle(DependencyObject d, object basevalue) - { - if (basevalue == null) - { - basevalue = (d as FrameworkElement).TryFindResource(typeof(SplitButton)); - } - - return basevalue; - } - - /// - /// Default constructor - /// - public SplitButton() - { - ContextMenuService.Coerce(this); - this.Click += this.OnClick; - // AddHandler(ClickEvent, OnClick); - - this.Loaded += this.OnLoaded; - this.Unloaded += this.OnUnloaded; - } - - private void OnLoaded(object sender, RoutedEventArgs e) - { - this.SubscribeEvents(); - } - - private void OnUnloaded(object sender, RoutedEventArgs e) - { - this.UnSubscribeEvents(); - } - - private void SubscribeEvents() - { - // Always unsubscribe events to ensure we don't subscribe twice - this.UnSubscribeEvents(); - - if (this.button != null) - { - this.button.Click += this.OnButtonClick; - } - } - - private void UnSubscribeEvents() - { - if (this.button != null) - { - this.button.Click -= this.OnButtonClick; - } - } - - private void OnClick(object sender, RoutedEventArgs e) - { - if (e.OriginalSource != this && e.OriginalSource != this._quickAccessButton) - { - e.Handled = true; - } - } - - #endregion - - #region Overrides - - /// - /// When overridden in a derived class, is invoked - /// whenever application code or internal processes call ApplyTemplate - /// - public override void OnApplyTemplate() - { - this.UnSubscribeEvents(); - - this.button = this.GetTemplateChild("PART_Button") as ToggleButton; - - base.OnApplyTemplate(); - - this.SubscribeEvents(); - } - /// - /// Invoked when an unhandled System.Windows.UIElement.PreviewMouseLeftButtonDown routed event - /// reaches an element in its route that is derived from this class. Implement this method to add - /// class handling for this event. - /// - /// The System.Windows.Input.MouseButtonEventArgs that contains the event data. - /// The event data reports that the left mouse button was pressed. - protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e) - { - if (!PopupService.IsMousePhysicallyOver(this.button)) - { - base.OnPreviewMouseLeftButtonDown(e); - } - else - { - this.IsDropDownOpen = false; - } - } - - #region Overrides of DropDownButton - - /// - /// Provides class handling for the routed event that occurs when the user presses a key. - /// - /// The event data for the event. - protected override void OnKeyDown(KeyEventArgs e) - { - base.OnKeyDown(e); - - if (e.Key == Key.Enter) - { - this.button.InvokeClick(); - } - } - - #endregion - - private void OnButtonClick(object sender, RoutedEventArgs e) - { - e.Handled = true; - this.RaiseEvent(new RoutedEventArgs(ClickEvent, this)); - } - - #endregion - - #region Quick Access Item Creating - - /// - /// Gets control which represents shortcut item. - /// This item MUST be synchronized with the original - /// and send command to original one control. - /// - /// Control which represents shortcut item - public override FrameworkElement CreateQuickAccessItem() - { - SplitButton button = new SplitButton(); - button.Click += ((sender, e) => this.RaiseEvent(e)); - RibbonProperties.SetSize(button, RibbonControlSize.Small); - button.CanAddButtonToQuickAccessToolBar = false; - this.BindQuickAccessItem(button); - this.BindQuickAccessItemDropDownEvents(button); - button.DropDownOpened += this.OnQuickAccessOpened; - this._quickAccessButton = button; - return button; - } - - /// - /// This method must be overridden to bind properties to use in quick access creating - /// - /// Toolbar item - protected override void BindQuickAccessItem(FrameworkElement element) - { - RibbonControl.Bind(this, element, "DisplayMemberPath", DisplayMemberPathProperty, BindingMode.OneWay); - RibbonControl.Bind(this, element, "GroupStyleSelector", GroupStyleSelectorProperty, BindingMode.OneWay); - RibbonControl.Bind(this, element, "ItemContainerStyle", ItemContainerStyleProperty, BindingMode.OneWay); - RibbonControl.Bind(this, element, "ItemsPanel", ItemsPanelProperty, BindingMode.OneWay); - RibbonControl.Bind(this, element, "ItemStringFormat", ItemStringFormatProperty, BindingMode.OneWay); - RibbonControl.Bind(this, element, "ItemTemplate", ItemTemplateProperty, BindingMode.OneWay); - RibbonControl.Bind(this, element, "MaxDropDownHeight", MaxDropDownHeightProperty, BindingMode.OneWay); - RibbonControl.Bind(this, element, "IsChecked", IsCheckedProperty, BindingMode.TwoWay); - RibbonControl.Bind(this, element, "DropDownToolTip", DropDownToolTipProperty, BindingMode.TwoWay); - RibbonControl.Bind(this, element, "IsCheckable", IsCheckableProperty, BindingMode.Default); - RibbonControl.Bind(this, element, "IsButtonEnabled", IsButtonEnabledProperty, BindingMode.Default); - RibbonControl.Bind(this, element, "ContextMenu", ContextMenuProperty, BindingMode.Default); - RibbonControl.BindQuickAccessItem(this, element); - RibbonControl.Bind(this, element, "ResizeMode", ResizeModeProperty, BindingMode.Default); - RibbonControl.Bind(this, element, "MaxDropDownHeight", MaxDropDownHeightProperty, BindingMode.Default); - RibbonControl.Bind(this, element, "HasTriangle", HasTriangleProperty, BindingMode.Default); - } - - /// - /// Gets or sets whether button can be added to quick access toolbar - /// - public bool CanAddButtonToQuickAccessToolBar - { - get { return (bool)this.GetValue(CanAddButtonToQuickAccessToolBarProperty); } - set { this.SetValue(CanAddButtonToQuickAccessToolBarProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for CanAddButtonToQuickAccessToolBar. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty CanAddButtonToQuickAccessToolBarProperty = DependencyProperty.Register("CanAddButtonToQuickAccessToolBar", typeof(bool), typeof(SplitButton), new UIPropertyMetadata(true, RibbonControl.OnCanAddToQuickAccessToolbarChanged)); - - private SplitButton _quickAccessButton; - - #endregion - } -} +using System.Collections; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Windows; +using System.Windows.Controls.Primitives; +using System.Windows.Data; +using System.Windows.Input; + +namespace Fluent +{ + /// + /// Represents button control that allows + /// you to add menu and handle clicks + /// + [TemplatePart(Name = "PART_Button", Type = typeof(ButtonBase))] + public class SplitButton : DropDownButton, IToggleButton, ICommandSource + { + #region Fields + + // Inner button + ToggleButton button; + + #endregion + + #region Properties + + /// + /// Gets an enumerator for logical child elements of this element. + /// + protected override IEnumerator LogicalChildren + { + get + { + var baseEnumerator = base.LogicalChildren; + while (baseEnumerator.MoveNext()) + { + yield return baseEnumerator.Current; + } + + if (this.button != null) + { + yield return this.button; + } + } + } + + #region Command + + /// + /// Gets or sets the command to invoke when this button is pressed. This is a dependency property. + /// + [Category("Action"), Localizability(LocalizationCategory.NeverLocalize), Bindable(true)] + public ICommand Command + { + get + { + return (ICommand)this.GetValue(CommandProperty); + } + set + { + this.SetValue(CommandProperty, value); + } + } + + /// + /// Gets or sets the parameter to pass to the System.Windows.Controls.Primitives.ButtonBase.Command property. This is a dependency property. + /// + [Bindable(true), Localizability(LocalizationCategory.NeverLocalize), Category("Action")] + public object CommandParameter + { + get + { + return this.GetValue(CommandParameterProperty); + } + set + { + this.SetValue(CommandParameterProperty, value); + } + } + + /// + /// Gets or sets the element on which to raise the specified command. This is a dependency property. + /// + [Bindable(true), Category("Action")] + public IInputElement CommandTarget + { + get + { + return (IInputElement)this.GetValue(CommandTargetProperty); + } + set + { + this.SetValue(CommandTargetProperty, value); + } + } + + /// + /// Identifies the CommandParameter dependency property. + /// + public static readonly DependencyProperty CommandParameterProperty = ButtonBase.CommandParameterProperty.AddOwner(typeof(SplitButton), new FrameworkPropertyMetadata(null)); + /// + /// Identifies the routed Command dependency property. + /// + public static readonly DependencyProperty CommandProperty = ButtonBase.CommandProperty.AddOwner(typeof(SplitButton), new FrameworkPropertyMetadata(null)); + + /// + /// Identifies the CommandTarget dependency property. + /// + public static readonly DependencyProperty CommandTargetProperty = ButtonBase.CommandTargetProperty.AddOwner(typeof(SplitButton), new FrameworkPropertyMetadata(null)); + + #endregion + + #region GroupName + + /// + /// Gets or sets the name of the group that the toggle button belongs to. + /// Use the GroupName property to specify a grouping of toggle buttons to + /// create a mutually exclusive set of controls. You can use the GroupName + /// property when only one selection is possible from a list of available + /// options. When this property is set, only one ToggleButton in the specified + /// group can be selected at a time. + /// + public string GroupName + { + get { return (string)this.GetValue(GroupNameProperty); } + set { this.SetValue(GroupNameProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for GroupName. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty GroupNameProperty = + DependencyProperty.Register("GroupName", typeof(string), typeof(SplitButton), + new UIPropertyMetadata(null, ToggleButtonHelper.OnGroupNameChanged)); + + #endregion + + #region IsChecked + + /// + /// Gets or sets a value indicating whether SplitButton is checked + /// + public bool? IsChecked + { + get { return (bool)this.GetValue(IsCheckedProperty); } + set { this.SetValue(IsCheckedProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for IsChecked. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IsCheckedProperty = + DependencyProperty.Register("IsChecked", typeof(bool?), typeof(SplitButton), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnIsCheckedChanged, CoerceIsChecked)); + + private static void OnIsCheckedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + SplitButton button = d as SplitButton; + if (button.IsCheckable) + { + if ((bool)e.NewValue) button.RaiseEvent(new RoutedEventArgs(CheckedEvent, button)); + else button.RaiseEvent(new RoutedEventArgs(UncheckedEvent, button)); + + ToggleButtonHelper.OnIsCheckedChanged(d, e); + } + } + + private static object CoerceIsChecked(DependencyObject d, object basevalue) + { + SplitButton button = d as SplitButton; + + if (!button.IsCheckable) return false; + + return ToggleButtonHelper.CoerceIsChecked(d, basevalue); + } + + #endregion + + #region IsCheckable + + /// + /// Gets or sets a value indicating whether SplitButton can be checked + /// + public bool IsCheckable + { + get { return (bool)this.GetValue(IsCheckableProperty); } + set { this.SetValue(IsCheckableProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for IsChecked. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IsCheckableProperty = + DependencyProperty.Register("IsCheckable", typeof(bool), typeof(SplitButton), new UIPropertyMetadata(false)); + + #endregion + + #region DropDownToolTip + + /// + /// Gets or sets tooltip of dropdown part of split button + /// + public object DropDownToolTip + { + get { return this.GetValue(DropDownToolTipProperty); } + set { this.SetValue(DropDownToolTipProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for DropDownToolTip. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty DropDownToolTipProperty = + DependencyProperty.Register("DropDownToolTip", typeof(object), typeof(SplitButton), new UIPropertyMetadata(null)); + + #endregion + + #region IsButtonEnabled + + /// + /// Gets or sets a value indicating whether dropdown part of split button is enabled + /// + public bool IsButtonEnabled + { + get { return (bool)this.GetValue(IsButtonEnabledProperty); } + set { this.SetValue(IsButtonEnabledProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for IsDropDownEnabled. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IsButtonEnabledProperty = + DependencyProperty.Register("IsButtonEnabled", typeof(bool), typeof(SplitButton), new UIPropertyMetadata(true)); + + #endregion + + #region IsDefinitive + + /// + /// Gets or sets whether ribbon control click must close backstage + /// + public bool IsDefinitive + { + get { return (bool)this.GetValue(IsDefinitiveProperty); } + set { this.SetValue(IsDefinitiveProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for IsDefinitive. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IsDefinitiveProperty = + DependencyProperty.Register("IsDefinitive", typeof(bool), typeof(SplitButton), new UIPropertyMetadata(true)); + + #endregion + + #endregion + + #region Events + + /// + /// Occurs when user clicks + /// + public static readonly RoutedEvent ClickEvent = ButtonBase.ClickEvent.AddOwner(typeof(SplitButton)); + + /// + /// Occurs when user clicks + /// + public event RoutedEventHandler Click + { + add + { + this.AddHandler(ClickEvent, value); + } + + remove + { + this.RemoveHandler(ClickEvent, value); + } + } + + /// + /// Occurs when button is checked + /// + public static readonly RoutedEvent CheckedEvent = System.Windows.Controls.Primitives.ToggleButton.CheckedEvent.AddOwner(typeof(SplitButton)); + + /// + /// Occurs when button is checked + /// + public event RoutedEventHandler Checked + { + add + { + this.AddHandler(CheckedEvent, value); + } + + remove + { + this.RemoveHandler(CheckedEvent, value); + } + } + + /// + /// Occurs when button is unchecked + /// + public static readonly RoutedEvent UncheckedEvent = System.Windows.Controls.Primitives.ToggleButton.UncheckedEvent.AddOwner(typeof(SplitButton)); + + /// + /// Occurs when button is unchecked + /// + public event RoutedEventHandler Unchecked + { + add + { + this.AddHandler(UncheckedEvent, value); + } + + remove + { + this.RemoveHandler(UncheckedEvent, value); + } + } + + #endregion + + #region Constructors + + [SuppressMessage("Microsoft.Performance", "CA1810")] + static SplitButton() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(SplitButton), new FrameworkPropertyMetadata(typeof(SplitButton))); + FocusVisualStyleProperty.OverrideMetadata(typeof(SplitButton), new FrameworkPropertyMetadata(null)); + StyleProperty.OverrideMetadata(typeof(SplitButton), new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceStyle))); + } + + // Coerce object style + static object OnCoerceStyle(DependencyObject d, object basevalue) + { + if (basevalue == null) + { + basevalue = (d as FrameworkElement).TryFindResource(typeof(SplitButton)); + } + + return basevalue; + } + + /// + /// Default constructor + /// + public SplitButton() + { + ContextMenuService.Coerce(this); + this.Click += this.OnClick; + // AddHandler(ClickEvent, OnClick); + + this.Loaded += this.OnLoaded; + this.Unloaded += this.OnUnloaded; + } + + private void OnLoaded(object sender, RoutedEventArgs e) + { + this.SubscribeEvents(); + } + + private void OnUnloaded(object sender, RoutedEventArgs e) + { + this.UnSubscribeEvents(); + } + + private void SubscribeEvents() + { + // Always unsubscribe events to ensure we don't subscribe twice + this.UnSubscribeEvents(); + + if (this.button != null) + { + this.button.Click += this.OnButtonClick; + } + } + + private void UnSubscribeEvents() + { + if (this.button != null) + { + this.button.Click -= this.OnButtonClick; + } + } + + private void OnClick(object sender, RoutedEventArgs e) + { + if (e.OriginalSource != this && e.OriginalSource != this._quickAccessButton) + { + e.Handled = true; + } + } + + #endregion + + #region Overrides + + /// + /// When overridden in a derived class, is invoked + /// whenever application code or internal processes call ApplyTemplate + /// + public override void OnApplyTemplate() + { + this.UnSubscribeEvents(); + + this.button = this.GetTemplateChild("PART_Button") as ToggleButton; + + base.OnApplyTemplate(); + + this.SubscribeEvents(); + } + /// + /// Invoked when an unhandled System.Windows.UIElement.PreviewMouseLeftButtonDown routed event + /// reaches an element in its route that is derived from this class. Implement this method to add + /// class handling for this event. + /// + /// The System.Windows.Input.MouseButtonEventArgs that contains the event data. + /// The event data reports that the left mouse button was pressed. + protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e) + { + if (!PopupService.IsMousePhysicallyOver(this.button)) + { + base.OnPreviewMouseLeftButtonDown(e); + } + else + { + this.IsDropDownOpen = false; + } + } + + #region Overrides of DropDownButton + + /// + /// Provides class handling for the routed event that occurs when the user presses a key. + /// + /// The event data for the event. + protected override void OnKeyDown(KeyEventArgs e) + { + base.OnKeyDown(e); + + if (e.Key == Key.Enter) + { + this.button.InvokeClick(); + } + } + + #endregion + + private void OnButtonClick(object sender, RoutedEventArgs e) + { + e.Handled = true; + this.RaiseEvent(new RoutedEventArgs(ClickEvent, this)); + } + + #endregion + + #region Quick Access Item Creating + + /// + /// Gets control which represents shortcut item. + /// This item MUST be synchronized with the original + /// and send command to original one control. + /// + /// Control which represents shortcut item + public override FrameworkElement CreateQuickAccessItem() + { + SplitButton button = new SplitButton(); + button.Click += ((sender, e) => this.RaiseEvent(e)); + RibbonProperties.SetSize(button, RibbonControlSize.Small); + button.CanAddButtonToQuickAccessToolBar = false; + this.BindQuickAccessItem(button); + this.BindQuickAccessItemDropDownEvents(button); + button.DropDownOpened += this.OnQuickAccessOpened; + this._quickAccessButton = button; + return button; + } + + /// + /// This method must be overridden to bind properties to use in quick access creating + /// + /// Toolbar item + protected override void BindQuickAccessItem(FrameworkElement element) + { + RibbonControl.Bind(this, element, "DisplayMemberPath", DisplayMemberPathProperty, BindingMode.OneWay); + RibbonControl.Bind(this, element, "GroupStyleSelector", GroupStyleSelectorProperty, BindingMode.OneWay); + RibbonControl.Bind(this, element, "ItemContainerStyle", ItemContainerStyleProperty, BindingMode.OneWay); + RibbonControl.Bind(this, element, "ItemsPanel", ItemsPanelProperty, BindingMode.OneWay); + RibbonControl.Bind(this, element, "ItemStringFormat", ItemStringFormatProperty, BindingMode.OneWay); + RibbonControl.Bind(this, element, "ItemTemplate", ItemTemplateProperty, BindingMode.OneWay); + RibbonControl.Bind(this, element, "MaxDropDownHeight", MaxDropDownHeightProperty, BindingMode.OneWay); + RibbonControl.Bind(this, element, "IsChecked", IsCheckedProperty, BindingMode.TwoWay); + RibbonControl.Bind(this, element, "DropDownToolTip", DropDownToolTipProperty, BindingMode.TwoWay); + RibbonControl.Bind(this, element, "IsCheckable", IsCheckableProperty, BindingMode.Default); + RibbonControl.Bind(this, element, "IsButtonEnabled", IsButtonEnabledProperty, BindingMode.Default); + RibbonControl.Bind(this, element, "ContextMenu", ContextMenuProperty, BindingMode.Default); + RibbonControl.BindQuickAccessItem(this, element); + RibbonControl.Bind(this, element, "ResizeMode", ResizeModeProperty, BindingMode.Default); + RibbonControl.Bind(this, element, "MaxDropDownHeight", MaxDropDownHeightProperty, BindingMode.Default); + RibbonControl.Bind(this, element, "HasTriangle", HasTriangleProperty, BindingMode.Default); + } + + /// + /// Gets or sets whether button can be added to quick access toolbar + /// + public bool CanAddButtonToQuickAccessToolBar + { + get { return (bool)this.GetValue(CanAddButtonToQuickAccessToolBarProperty); } + set { this.SetValue(CanAddButtonToQuickAccessToolBarProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for CanAddButtonToQuickAccessToolBar. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty CanAddButtonToQuickAccessToolBarProperty = DependencyProperty.Register("CanAddButtonToQuickAccessToolBar", typeof(bool), typeof(SplitButton), new UIPropertyMetadata(true, RibbonControl.OnCanAddToQuickAccessToolbarChanged)); + + private SplitButton _quickAccessButton; + + #endregion + } +} diff --git a/Fluent/Controls/StatusBar.cs b/Fluent.Ribbon/Controls/StatusBar.cs similarity index 97% rename from Fluent/Controls/StatusBar.cs rename to Fluent.Ribbon/Controls/StatusBar.cs index c7d485e60..26ca307cb 100644 --- a/Fluent/Controls/StatusBar.cs +++ b/Fluent.Ribbon/Controls/StatusBar.cs @@ -1,354 +1,354 @@ -using System; -using System.Collections.Specialized; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Controls.Primitives; -using System.Windows.Data; -using System.Windows.Threading; - -namespace Fluent -{ - /// - /// Represents ribbon status bar - /// - public class StatusBar : System.Windows.Controls.Primitives.StatusBar - { - #region Fields - - // Context menu - private readonly ContextMenu contextMenu = new ContextMenu(); - - private Window ownerWindow; - - #endregion - - #region Properties - - /// - /// Gets or sets whether window is maximized - /// - public bool IsWindowMaximized - { - get { return (bool)this.GetValue(IsWindowMaximizedProperty); } - set { this.SetValue(IsWindowMaximizedProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for IsWindowMaximized. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IsWindowMaximizedProperty = - DependencyProperty.Register("IsWindowMaximized", typeof(bool), typeof(StatusBar), new UIPropertyMetadata(false)); - -#if NET45 - private object currentItem; -#endif - - #endregion - - #region Constructors - - /// - /// Static constructor - /// - static StatusBar() - { - DefaultStyleKeyProperty.OverrideMetadata(typeof(StatusBar), new FrameworkPropertyMetadata(typeof(StatusBar))); - } - - /// - /// Default constructor - /// - public StatusBar() - { - this.RecreateMenu(); - this.ContextMenu = this.contextMenu; - - this.Loaded += this.OnLoaded; - this.Unloaded += this.OnUnloaded; - - this.ItemContainerGenerator.StatusChanged += this.HandleItemContainerGeneratorStatusChanged; - } - - private void OnUnloaded(object sender, RoutedEventArgs e) - { - if (this.ownerWindow != null) - { - this.ownerWindow.StateChanged -= this.OnWindowStateChanged; - this.ownerWindow = null; - } - } - - private void OnLoaded(object sender, RoutedEventArgs e) - { - if (this.ownerWindow == null) - { - this.ownerWindow = Window.GetWindow(this); - } - - if (this.ownerWindow != null) - { - this.ownerWindow.StateChanged += this.OnWindowStateChanged; - if ((this.ownerWindow.ResizeMode == ResizeMode.CanResizeWithGrip) && (this.ownerWindow.WindowState == WindowState.Maximized)) - { - this.IsWindowMaximized = true; - } - else - { - this.IsWindowMaximized = false; - } - } - } - - private void OnWindowStateChanged(object sender, EventArgs e) - { - if ((this.ownerWindow.ResizeMode == ResizeMode.CanResizeWithGrip) && (this.ownerWindow.WindowState == WindowState.Maximized)) - { - this.IsWindowMaximized = true; - } - else - { - this.IsWindowMaximized = false; - } - } - - #endregion - - #region Overrides - - /// - /// Creates or identifies the element that is used to display the given item. - /// - /// The element that is used to display the given item. - protected override DependencyObject GetContainerForItemOverride() - { -#if NET45 - var item = this.currentItem; - this.currentItem = null; - - if (this.UsesItemContainerTemplate - && item != null) - { - var dataTemplate = this.ItemContainerTemplateSelector.SelectTemplate(item, this); - if (dataTemplate != null) - { - var dataTemplateContent = (object)dataTemplate.LoadContent(); - if (dataTemplateContent is StatusBarItem - || dataTemplateContent is Separator) - { - return dataTemplateContent as DependencyObject; - } - - throw new InvalidOperationException("Invalid ItemContainer"); - } - } -#endif - return new StatusBarItem(); - } - - /// - /// Determines if the specified item is (or is eligible to be) its own container. - /// - /// The item to check. - /// true if the item is (or is eligible to be) its own container; otherwise, false. - protected override bool IsItemItsOwnContainerOverride(object item) - { - var isItemItsOwnContainerOverride = item is StatusBarItem || item is Separator; - -#if NET45 - if (isItemItsOwnContainerOverride == false) - { - this.currentItem = item; - } -#endif - - return isItemItsOwnContainerOverride; - } - - private void HandleItemContainerGeneratorStatusChanged(object sender, EventArgs e) - { - if (this.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated) - { - return; - } - - this.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, (Delegate)new Action(this.RecreateMenu)); - } - - /// - /// Invoked when the property changes. - /// - /// Information about the change. - protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e) - { - base.OnItemsChanged(e); - - if (this.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated) - { - return; - } - - switch (e.Action) - { - case NotifyCollectionChangedAction.Add: - { - for (var i = 0; i < e.NewItems.Count; i++) - { - var container = this.ItemContainerGenerator.ContainerFromItem(e.NewItems[i]); - var item = container as StatusBarItem; - - if (item != null) - { - item.Checked += this.OnItemChecked; - item.Unchecked += this.OnItemUnchecked; - this.contextMenu.Items.Insert(this.ItemContainerGenerator.IndexFromContainer(container), new StatusBarMenuItem(item)); - } - else - { - this.contextMenu.Items.Insert(this.ItemContainerGenerator.IndexFromContainer(container), new Separator()); - } - } - break; - } - - case NotifyCollectionChangedAction.Move: - { - for (var i = 0; i < e.NewItems.Count; i++) - { - var menuItem = this.contextMenu.Items[e.OldStartingIndex + 1]; - this.contextMenu.Items.Remove(e.OldStartingIndex + 1); - this.contextMenu.Items.Insert(e.NewStartingIndex + i + 1, menuItem); - } - break; - } - case NotifyCollectionChangedAction.Remove: - { - for (var i = 0; i < e.OldItems.Count; i++) - { - var menuItem = this.contextMenu.Items[e.OldStartingIndex + 1] as StatusBarMenuItem; - if (menuItem != null) - { - menuItem.StatusBarItem.Checked += this.OnItemChecked; - menuItem.StatusBarItem.Unchecked += this.OnItemUnchecked; - } - this.contextMenu.Items.RemoveAt(e.OldStartingIndex + 1); - } - break; - } - case NotifyCollectionChangedAction.Replace: - { - for (var i = 0; i < e.OldItems.Count; i++) - { - var menuItem = this.contextMenu.Items[e.OldStartingIndex + 1] as StatusBarMenuItem; - if (menuItem != null) - { - menuItem.StatusBarItem.Checked += this.OnItemChecked; - menuItem.StatusBarItem.Unchecked += this.OnItemUnchecked; - } - - this.contextMenu.Items.RemoveAt(e.OldStartingIndex + 1); - } - - for (var i = 0; i < e.NewItems.Count; i++) - { - var item = this.ItemContainerGenerator.ContainerFromItem(e.NewItems[i]) as StatusBarItem; - if (item != null) - { - item.Checked += this.OnItemChecked; - item.Unchecked += this.OnItemUnchecked; - this.contextMenu.Items.Insert(e.NewStartingIndex + i + 1, new StatusBarMenuItem(item)); - } - else - { - this.contextMenu.Items.Insert(e.NewStartingIndex + i + 1, new Separator()); - } - } - break; - } - case NotifyCollectionChangedAction.Reset: - { - this.RecreateMenu(); - break; - } - } - } - - private void OnItemUnchecked(object sender, RoutedEventArgs e) - { - this.UpdateSeparartorsVisibility(); - } - - private void OnItemChecked(object sender, RoutedEventArgs e) - { - this.UpdateSeparartorsVisibility(); - } - - #endregion - - #region Private Methods - - // Creates menu - private void RecreateMenu() - { - this.contextMenu.Items.Clear(); - - // Adding header separator - this.contextMenu.Items.Add(new GroupSeparatorMenuItem()); - RibbonControl.Bind(Ribbon.Localization, this.contextMenu.Items[0] as FrameworkElement, "CustomizeStatusBar", HeaderedItemsControl.HeaderProperty, BindingMode.OneWay); - - for (var i = 0; i < this.Items.Count; i++) - { - var item = this.ItemContainerGenerator.ContainerFromItem(this.Items[i]) as StatusBarItem; - if (item != null) - { - item.Checked += this.OnItemChecked; - item.Unchecked += this.OnItemUnchecked; - this.contextMenu.Items.Add(new StatusBarMenuItem(item)); - } - else - { - this.contextMenu.Items.Add(new Separator()); - } - } - - this.UpdateSeparartorsVisibility(); - } - - // Updates separators visibility, to not duplicate - private void UpdateSeparartorsVisibility() - { - var isPrevSeparator = false; - var isFirstVsible = true; - - for (var i = 0; i < this.Items.Count; i++) - { - var item = this.ItemContainerGenerator.ContainerFromItem(this.Items[i]); - var separator = item as Separator; - - if (separator != null) - { - if (isPrevSeparator || isFirstVsible) - { - separator.Visibility = Visibility.Collapsed; - } - else - { - separator.Visibility = Visibility.Visible; - } - - isPrevSeparator = true; - isFirstVsible = false; - } - else if (item is StatusBarItem) - { - if ((item as StatusBarItem).Visibility == Visibility.Visible) - { - isPrevSeparator = false; - isFirstVsible = false; - } - } - } - } - - #endregion - } +using System; +using System.Collections.Specialized; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Data; +using System.Windows.Threading; + +namespace Fluent +{ + /// + /// Represents ribbon status bar + /// + public class StatusBar : System.Windows.Controls.Primitives.StatusBar + { + #region Fields + + // Context menu + private readonly ContextMenu contextMenu = new ContextMenu(); + + private Window ownerWindow; + + #endregion + + #region Properties + + /// + /// Gets or sets whether window is maximized + /// + public bool IsWindowMaximized + { + get { return (bool)this.GetValue(IsWindowMaximizedProperty); } + set { this.SetValue(IsWindowMaximizedProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for IsWindowMaximized. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IsWindowMaximizedProperty = + DependencyProperty.Register("IsWindowMaximized", typeof(bool), typeof(StatusBar), new UIPropertyMetadata(false)); + +#if NET45 + private object currentItem; +#endif + + #endregion + + #region Constructors + + /// + /// Static constructor + /// + static StatusBar() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(StatusBar), new FrameworkPropertyMetadata(typeof(StatusBar))); + } + + /// + /// Default constructor + /// + public StatusBar() + { + this.RecreateMenu(); + this.ContextMenu = this.contextMenu; + + this.Loaded += this.OnLoaded; + this.Unloaded += this.OnUnloaded; + + this.ItemContainerGenerator.StatusChanged += this.HandleItemContainerGeneratorStatusChanged; + } + + private void OnUnloaded(object sender, RoutedEventArgs e) + { + if (this.ownerWindow != null) + { + this.ownerWindow.StateChanged -= this.OnWindowStateChanged; + this.ownerWindow = null; + } + } + + private void OnLoaded(object sender, RoutedEventArgs e) + { + if (this.ownerWindow == null) + { + this.ownerWindow = Window.GetWindow(this); + } + + if (this.ownerWindow != null) + { + this.ownerWindow.StateChanged += this.OnWindowStateChanged; + if ((this.ownerWindow.ResizeMode == ResizeMode.CanResizeWithGrip) && (this.ownerWindow.WindowState == WindowState.Maximized)) + { + this.IsWindowMaximized = true; + } + else + { + this.IsWindowMaximized = false; + } + } + } + + private void OnWindowStateChanged(object sender, EventArgs e) + { + if ((this.ownerWindow.ResizeMode == ResizeMode.CanResizeWithGrip) && (this.ownerWindow.WindowState == WindowState.Maximized)) + { + this.IsWindowMaximized = true; + } + else + { + this.IsWindowMaximized = false; + } + } + + #endregion + + #region Overrides + + /// + /// Creates or identifies the element that is used to display the given item. + /// + /// The element that is used to display the given item. + protected override DependencyObject GetContainerForItemOverride() + { +#if NET45 + var item = this.currentItem; + this.currentItem = null; + + if (this.UsesItemContainerTemplate + && item != null) + { + var dataTemplate = this.ItemContainerTemplateSelector.SelectTemplate(item, this); + if (dataTemplate != null) + { + var dataTemplateContent = (object)dataTemplate.LoadContent(); + if (dataTemplateContent is StatusBarItem + || dataTemplateContent is Separator) + { + return dataTemplateContent as DependencyObject; + } + + throw new InvalidOperationException("Invalid ItemContainer"); + } + } +#endif + return new StatusBarItem(); + } + + /// + /// Determines if the specified item is (or is eligible to be) its own container. + /// + /// The item to check. + /// true if the item is (or is eligible to be) its own container; otherwise, false. + protected override bool IsItemItsOwnContainerOverride(object item) + { + var isItemItsOwnContainerOverride = item is StatusBarItem || item is Separator; + +#if NET45 + if (isItemItsOwnContainerOverride == false) + { + this.currentItem = item; + } +#endif + + return isItemItsOwnContainerOverride; + } + + private void HandleItemContainerGeneratorStatusChanged(object sender, EventArgs e) + { + if (this.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated) + { + return; + } + + this.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, (Delegate)new Action(this.RecreateMenu)); + } + + /// + /// Invoked when the property changes. + /// + /// Information about the change. + protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e) + { + base.OnItemsChanged(e); + + if (this.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated) + { + return; + } + + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + { + for (var i = 0; i < e.NewItems.Count; i++) + { + var container = this.ItemContainerGenerator.ContainerFromItem(e.NewItems[i]); + var item = container as StatusBarItem; + + if (item != null) + { + item.Checked += this.OnItemChecked; + item.Unchecked += this.OnItemUnchecked; + this.contextMenu.Items.Insert(this.ItemContainerGenerator.IndexFromContainer(container), new StatusBarMenuItem(item)); + } + else + { + this.contextMenu.Items.Insert(this.ItemContainerGenerator.IndexFromContainer(container), new Separator()); + } + } + break; + } + + case NotifyCollectionChangedAction.Move: + { + for (var i = 0; i < e.NewItems.Count; i++) + { + var menuItem = this.contextMenu.Items[e.OldStartingIndex + 1]; + this.contextMenu.Items.Remove(e.OldStartingIndex + 1); + this.contextMenu.Items.Insert(e.NewStartingIndex + i + 1, menuItem); + } + break; + } + case NotifyCollectionChangedAction.Remove: + { + for (var i = 0; i < e.OldItems.Count; i++) + { + var menuItem = this.contextMenu.Items[e.OldStartingIndex + 1] as StatusBarMenuItem; + if (menuItem != null) + { + menuItem.StatusBarItem.Checked += this.OnItemChecked; + menuItem.StatusBarItem.Unchecked += this.OnItemUnchecked; + } + this.contextMenu.Items.RemoveAt(e.OldStartingIndex + 1); + } + break; + } + case NotifyCollectionChangedAction.Replace: + { + for (var i = 0; i < e.OldItems.Count; i++) + { + var menuItem = this.contextMenu.Items[e.OldStartingIndex + 1] as StatusBarMenuItem; + if (menuItem != null) + { + menuItem.StatusBarItem.Checked += this.OnItemChecked; + menuItem.StatusBarItem.Unchecked += this.OnItemUnchecked; + } + + this.contextMenu.Items.RemoveAt(e.OldStartingIndex + 1); + } + + for (var i = 0; i < e.NewItems.Count; i++) + { + var item = this.ItemContainerGenerator.ContainerFromItem(e.NewItems[i]) as StatusBarItem; + if (item != null) + { + item.Checked += this.OnItemChecked; + item.Unchecked += this.OnItemUnchecked; + this.contextMenu.Items.Insert(e.NewStartingIndex + i + 1, new StatusBarMenuItem(item)); + } + else + { + this.contextMenu.Items.Insert(e.NewStartingIndex + i + 1, new Separator()); + } + } + break; + } + case NotifyCollectionChangedAction.Reset: + { + this.RecreateMenu(); + break; + } + } + } + + private void OnItemUnchecked(object sender, RoutedEventArgs e) + { + this.UpdateSeparartorsVisibility(); + } + + private void OnItemChecked(object sender, RoutedEventArgs e) + { + this.UpdateSeparartorsVisibility(); + } + + #endregion + + #region Private Methods + + // Creates menu + private void RecreateMenu() + { + this.contextMenu.Items.Clear(); + + // Adding header separator + this.contextMenu.Items.Add(new GroupSeparatorMenuItem()); + RibbonControl.Bind(Ribbon.Localization, this.contextMenu.Items[0] as FrameworkElement, "CustomizeStatusBar", HeaderedItemsControl.HeaderProperty, BindingMode.OneWay); + + for (var i = 0; i < this.Items.Count; i++) + { + var item = this.ItemContainerGenerator.ContainerFromItem(this.Items[i]) as StatusBarItem; + if (item != null) + { + item.Checked += this.OnItemChecked; + item.Unchecked += this.OnItemUnchecked; + this.contextMenu.Items.Add(new StatusBarMenuItem(item)); + } + else + { + this.contextMenu.Items.Add(new Separator()); + } + } + + this.UpdateSeparartorsVisibility(); + } + + // Updates separators visibility, to not duplicate + private void UpdateSeparartorsVisibility() + { + var isPrevSeparator = false; + var isFirstVsible = true; + + for (var i = 0; i < this.Items.Count; i++) + { + var item = this.ItemContainerGenerator.ContainerFromItem(this.Items[i]); + var separator = item as Separator; + + if (separator != null) + { + if (isPrevSeparator || isFirstVsible) + { + separator.Visibility = Visibility.Collapsed; + } + else + { + separator.Visibility = Visibility.Visible; + } + + isPrevSeparator = true; + isFirstVsible = false; + } + else if (item is StatusBarItem) + { + if ((item as StatusBarItem).Visibility == Visibility.Visible) + { + isPrevSeparator = false; + isFirstVsible = false; + } + } + } + } + + #endregion + } } \ No newline at end of file diff --git a/Fluent/Controls/StatusBarItem.cs b/Fluent.Ribbon/Controls/StatusBarItem.cs similarity index 97% rename from Fluent/Controls/StatusBarItem.cs rename to Fluent.Ribbon/Controls/StatusBarItem.cs index 95631cdf6..832ba9259 100644 --- a/Fluent/Controls/StatusBarItem.cs +++ b/Fluent.Ribbon/Controls/StatusBarItem.cs @@ -1,159 +1,159 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Windows; -using System.Windows.Controls.Primitives; - -namespace Fluent -{ - /// - /// Represents ribbon status bar item - /// - public class StatusBarItem : System.Windows.Controls.Primitives.StatusBarItem - { - #region Properties - - #region Title - - /// - /// Gets or sets ribbon status bar item - /// - public string Title - { - get { return (string)this.GetValue(TitleProperty); } - set { this.SetValue(TitleProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Title. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty TitleProperty = - DependencyProperty.Register("Title", typeof(string), typeof(StatusBarItem), new UIPropertyMetadata(null)); - - #endregion - - #region Value - - /// - /// Gets or sets ribbon status bar value - /// - public string Value - { - get { return (string)this.GetValue(ValueProperty); } - set { this.SetValue(ValueProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Value. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty ValueProperty = - DependencyProperty.Register("Value", typeof(string), typeof(StatusBarItem), - new UIPropertyMetadata(null, OnValueChanged)); - - static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - StatusBarItem item = (StatusBarItem)d; - item.CoerceValue(ContentProperty); - } - - - #endregion - - #region isChecked - - /// - /// Gets or sets whether status bar item is checked in menu - /// - public bool IsChecked - { - get { return (bool)this.GetValue(IsCheckedProperty); } - set { this.SetValue(IsCheckedProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for IsChecked. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IsCheckedProperty = - DependencyProperty.Register("IsChecked", typeof(bool), typeof(StatusBarItem), new UIPropertyMetadata(true, OnIsCheckedChanged)); - - // Handles IsChecked changed - private static void OnIsCheckedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - StatusBarItem item = d as StatusBarItem; - item.CoerceValue(VisibilityProperty); - if((bool)e.NewValue) item.RaiseChecked(); - else item.RaiseUnchecked(); - } - - #endregion - - #endregion - - #region Events - - /// - /// Occurs when status bar item checks - /// - public event RoutedEventHandler Checked; - /// - /// Occurs when status bar item unchecks - /// - public event RoutedEventHandler Unchecked; - - // Raises checked event - private void RaiseChecked() - { - if (this.Checked != null) - this.Checked(this, new RoutedEventArgs()); - } - - // Raises unchecked event - private void RaiseUnchecked() - { - if (this.Unchecked != null) - this.Unchecked(this, new RoutedEventArgs()); - } - - #endregion - - #region Constructors - - /// - /// Static constructor - /// - static StatusBarItem() - { - DefaultStyleKeyProperty.OverrideMetadata(typeof(StatusBarItem), new FrameworkPropertyMetadata(typeof(StatusBarItem))); - VisibilityProperty.AddOwner(typeof(StatusBarItem),new FrameworkPropertyMetadata(null, CoerceVisibility)); - ContentProperty.AddOwner(typeof (StatusBarItem), new FrameworkPropertyMetadata(null, OnContentChanged, CoerceContent)); - } - - // Content changing handler - static void OnContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - StatusBarItem item = (StatusBarItem)d; - item.CoerceValue(ValueProperty); - } - - // Coerce content - static object CoerceContent(DependencyObject d, object basevalue) - { - StatusBarItem item = (StatusBarItem)d; - // if content is null returns value - if ((basevalue == null) && (item.Value != null)) return item.Value; - return basevalue; - } - - // Coerce visibility - static object CoerceVisibility(DependencyObject d, object basevalue) - { - // If unchecked when not visible in status bar - if (!(d as StatusBarItem).IsChecked) return Visibility.Collapsed; - return basevalue; - } - - #endregion - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls.Primitives; + +namespace Fluent +{ + /// + /// Represents ribbon status bar item + /// + public class StatusBarItem : System.Windows.Controls.Primitives.StatusBarItem + { + #region Properties + + #region Title + + /// + /// Gets or sets ribbon status bar item + /// + public string Title + { + get { return (string)this.GetValue(TitleProperty); } + set { this.SetValue(TitleProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Title. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty TitleProperty = + DependencyProperty.Register("Title", typeof(string), typeof(StatusBarItem), new UIPropertyMetadata(null)); + + #endregion + + #region Value + + /// + /// Gets or sets ribbon status bar value + /// + public string Value + { + get { return (string)this.GetValue(ValueProperty); } + set { this.SetValue(ValueProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Value. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty ValueProperty = + DependencyProperty.Register("Value", typeof(string), typeof(StatusBarItem), + new UIPropertyMetadata(null, OnValueChanged)); + + static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + StatusBarItem item = (StatusBarItem)d; + item.CoerceValue(ContentProperty); + } + + + #endregion + + #region isChecked + + /// + /// Gets or sets whether status bar item is checked in menu + /// + public bool IsChecked + { + get { return (bool)this.GetValue(IsCheckedProperty); } + set { this.SetValue(IsCheckedProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for IsChecked. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IsCheckedProperty = + DependencyProperty.Register("IsChecked", typeof(bool), typeof(StatusBarItem), new UIPropertyMetadata(true, OnIsCheckedChanged)); + + // Handles IsChecked changed + private static void OnIsCheckedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + StatusBarItem item = d as StatusBarItem; + item.CoerceValue(VisibilityProperty); + if((bool)e.NewValue) item.RaiseChecked(); + else item.RaiseUnchecked(); + } + + #endregion + + #endregion + + #region Events + + /// + /// Occurs when status bar item checks + /// + public event RoutedEventHandler Checked; + /// + /// Occurs when status bar item unchecks + /// + public event RoutedEventHandler Unchecked; + + // Raises checked event + private void RaiseChecked() + { + if (this.Checked != null) + this.Checked(this, new RoutedEventArgs()); + } + + // Raises unchecked event + private void RaiseUnchecked() + { + if (this.Unchecked != null) + this.Unchecked(this, new RoutedEventArgs()); + } + + #endregion + + #region Constructors + + /// + /// Static constructor + /// + static StatusBarItem() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(StatusBarItem), new FrameworkPropertyMetadata(typeof(StatusBarItem))); + VisibilityProperty.AddOwner(typeof(StatusBarItem),new FrameworkPropertyMetadata(null, CoerceVisibility)); + ContentProperty.AddOwner(typeof (StatusBarItem), new FrameworkPropertyMetadata(null, OnContentChanged, CoerceContent)); + } + + // Content changing handler + static void OnContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + StatusBarItem item = (StatusBarItem)d; + item.CoerceValue(ValueProperty); + } + + // Coerce content + static object CoerceContent(DependencyObject d, object basevalue) + { + StatusBarItem item = (StatusBarItem)d; + // if content is null returns value + if ((basevalue == null) && (item.Value != null)) return item.Value; + return basevalue; + } + + // Coerce visibility + static object CoerceVisibility(DependencyObject d, object basevalue) + { + // If unchecked when not visible in status bar + if (!(d as StatusBarItem).IsChecked) return Visibility.Collapsed; + return basevalue; + } + + #endregion + } +} diff --git a/Fluent/Controls/StatusBarMenuItem.cs b/Fluent.Ribbon/Controls/StatusBarMenuItem.cs similarity index 96% rename from Fluent/Controls/StatusBarMenuItem.cs rename to Fluent.Ribbon/Controls/StatusBarMenuItem.cs index f53729903..b6c4d6cfe 100644 --- a/Fluent/Controls/StatusBarMenuItem.cs +++ b/Fluent.Ribbon/Controls/StatusBarMenuItem.cs @@ -1,56 +1,56 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Windows; -using Fluent; - -namespace Fluent -{ - /// - /// Represents menu item in ribbon status bar menu - /// - public class StatusBarMenuItem : MenuItem - { - #region Properties - - /// - /// Gets or sets Ribbon Status Bar menu item - /// - public StatusBarItem StatusBarItem - { - get { return (StatusBarItem)this.GetValue(StatusBarItemProperty); } - set { this.SetValue(StatusBarItemProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for StatusBarItem. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty StatusBarItemProperty = - DependencyProperty.Register("StatusBarItem", typeof(StatusBarItem), typeof(StatusBarMenuItem), new UIPropertyMetadata(null)); - - - #endregion - - #region Constructors - - /// - /// Static constructor - /// - static StatusBarMenuItem() - { - DefaultStyleKeyProperty.OverrideMetadata(typeof(StatusBarMenuItem), new FrameworkPropertyMetadata(typeof(StatusBarMenuItem))); - } - - /// - /// Default constructor - /// - /// Ribbon Status Bar menu item - public StatusBarMenuItem(StatusBarItem item) - { - this.StatusBarItem = item; - } - - #endregion - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using Fluent; + +namespace Fluent +{ + /// + /// Represents menu item in ribbon status bar menu + /// + public class StatusBarMenuItem : MenuItem + { + #region Properties + + /// + /// Gets or sets Ribbon Status Bar menu item + /// + public StatusBarItem StatusBarItem + { + get { return (StatusBarItem)this.GetValue(StatusBarItemProperty); } + set { this.SetValue(StatusBarItemProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for StatusBarItem. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty StatusBarItemProperty = + DependencyProperty.Register("StatusBarItem", typeof(StatusBarItem), typeof(StatusBarMenuItem), new UIPropertyMetadata(null)); + + + #endregion + + #region Constructors + + /// + /// Static constructor + /// + static StatusBarMenuItem() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(StatusBarMenuItem), new FrameworkPropertyMetadata(typeof(StatusBarMenuItem))); + } + + /// + /// Default constructor + /// + /// Ribbon Status Bar menu item + public StatusBarMenuItem(StatusBarItem item) + { + this.StatusBarItem = item; + } + + #endregion + } +} diff --git a/Fluent/Controls/StatusBarPanel.cs b/Fluent.Ribbon/Controls/StatusBarPanel.cs similarity index 97% rename from Fluent/Controls/StatusBarPanel.cs rename to Fluent.Ribbon/Controls/StatusBarPanel.cs index 54840d77b..97fbfb6de 100644 --- a/Fluent/Controls/StatusBarPanel.cs +++ b/Fluent.Ribbon/Controls/StatusBarPanel.cs @@ -1,173 +1,173 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Windows; -using System.Windows.Controls; - -namespace Fluent -{ - /// - /// Represents panel for status bar - /// - public class StatusBarPanel: Panel - { - #region Attributes - - private List leftChildren = new List(); - private List rightChildren = new List(); - private List otherChildren = new List(); - - private int lastRightIndex = 0; - private int lastLeftIndex = 0; - - #endregion - - #region Overrides - - /// - /// When overridden in a derived class, measures the size in layout required for child elements and determines a size for the -derived class. - /// - /// - /// The size that this element determines it needs during layout, based on its calculations of child element sizes. - /// - /// The available size that this element can give to child elements. Infinity can be specified as a value to indicate that the element will size to whatever content is available. - protected override Size MeasureOverride(Size availableSize) - { - // Sort children - this.leftChildren.Clear(); - this.rightChildren.Clear(); - this.otherChildren.Clear(); - for (int i = 0; i < this.InternalChildren.Count; i++) - { - FrameworkElement child = this.InternalChildren[i] as FrameworkElement; - if (child != null) - { - if (child.HorizontalAlignment == HorizontalAlignment.Left) - this.leftChildren.Add(child); - else if (child.HorizontalAlignment == HorizontalAlignment.Right) - this.rightChildren.Add(child); - else - this.otherChildren.Add(child); - } - } - - this.lastRightIndex = this.rightChildren.Count; - this.lastLeftIndex = this.leftChildren.Count; - - // Measure children - Size infinity = new Size(double.PositiveInfinity, double.PositiveInfinity); - Size zero = new Size(0, 0); - double width = 0; - double height = 0; - bool canAdd = true; - // Right children - for (int i = 0; i < this.rightChildren.Count; i++) - { - if (canAdd) - { - this.rightChildren[i].Measure(infinity); - height = Math.Max(this.rightChildren[i].DesiredSize.Height, height); - if (width + this.rightChildren[i].DesiredSize.Width <= availableSize.Width) - { - width += this.rightChildren[i].DesiredSize.Width; - } - else - { - canAdd = false; - this.rightChildren[i].Measure(zero); - this.lastRightIndex = i; - this.lastLeftIndex = 0; - } - } - else - { - this.rightChildren[i].Measure(zero); - } - } - - // Left children - for (int i = 0; i < this.leftChildren.Count; i++) - { - if (canAdd) - { - this.leftChildren[i].Measure(infinity); - height = Math.Max(this.leftChildren[i].DesiredSize.Height, height); - if (width + this.leftChildren[i].DesiredSize.Width <= availableSize.Width) - { - width += this.leftChildren[i].DesiredSize.Width; - } - else - { - canAdd = false; - this.leftChildren[i].Measure(zero); - this.lastLeftIndex = i; - - } - } - else - { - this.leftChildren[i].Measure(zero); - } - } - - // Collapse other children - for (int i = 0; i < this.otherChildren.Count; i++) - { - this.otherChildren[i].Measure(zero); - } - - return new Size(width, height); - } - - /// - /// When overridden in a derived class, positions child elements and determines a size for a derived class. - /// - /// - /// The actual size used. - /// - /// The final area within the parent that this element should use to arrange itself and its children. - protected override Size ArrangeOverride(Size finalSize) - { - Rect zero = new Rect(0, 0, 0, 0); - - // Right shift - double rightShift = 0; - // Arrange right - for (int i = this.rightChildren.Count - 1; i >= 0; i--) - { - if (this.lastRightIndex > i) - { - rightShift += this.rightChildren[i].DesiredSize.Width; - this.rightChildren[i].Arrange(new Rect(finalSize.Width - rightShift, 0, this.rightChildren[i].DesiredSize.Width, finalSize.Height)); - } - else - this.rightChildren[i].Arrange(zero); - } - - // Left shift - double leftShift = 0; - // Arrange left - for (int i = 0; i < this.leftChildren.Count; i++) - { - if (i < this.lastLeftIndex) - { - this.leftChildren[i].Arrange(new Rect(leftShift, 0, this.leftChildren[i].DesiredSize.Width, finalSize.Height)); - leftShift += this.leftChildren[i].DesiredSize.Width; - } - else - this.leftChildren[i].Arrange(zero); - } - - // Arrange other - for (int i = 0; i < this.otherChildren.Count; i++) - { - this.otherChildren[i].Arrange(zero); - } - - return finalSize; - } - - #endregion - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; + +namespace Fluent +{ + /// + /// Represents panel for status bar + /// + public class StatusBarPanel: Panel + { + #region Attributes + + private List leftChildren = new List(); + private List rightChildren = new List(); + private List otherChildren = new List(); + + private int lastRightIndex = 0; + private int lastLeftIndex = 0; + + #endregion + + #region Overrides + + /// + /// When overridden in a derived class, measures the size in layout required for child elements and determines a size for the -derived class. + /// + /// + /// The size that this element determines it needs during layout, based on its calculations of child element sizes. + /// + /// The available size that this element can give to child elements. Infinity can be specified as a value to indicate that the element will size to whatever content is available. + protected override Size MeasureOverride(Size availableSize) + { + // Sort children + this.leftChildren.Clear(); + this.rightChildren.Clear(); + this.otherChildren.Clear(); + for (int i = 0; i < this.InternalChildren.Count; i++) + { + FrameworkElement child = this.InternalChildren[i] as FrameworkElement; + if (child != null) + { + if (child.HorizontalAlignment == HorizontalAlignment.Left) + this.leftChildren.Add(child); + else if (child.HorizontalAlignment == HorizontalAlignment.Right) + this.rightChildren.Add(child); + else + this.otherChildren.Add(child); + } + } + + this.lastRightIndex = this.rightChildren.Count; + this.lastLeftIndex = this.leftChildren.Count; + + // Measure children + Size infinity = new Size(double.PositiveInfinity, double.PositiveInfinity); + Size zero = new Size(0, 0); + double width = 0; + double height = 0; + bool canAdd = true; + // Right children + for (int i = 0; i < this.rightChildren.Count; i++) + { + if (canAdd) + { + this.rightChildren[i].Measure(infinity); + height = Math.Max(this.rightChildren[i].DesiredSize.Height, height); + if (width + this.rightChildren[i].DesiredSize.Width <= availableSize.Width) + { + width += this.rightChildren[i].DesiredSize.Width; + } + else + { + canAdd = false; + this.rightChildren[i].Measure(zero); + this.lastRightIndex = i; + this.lastLeftIndex = 0; + } + } + else + { + this.rightChildren[i].Measure(zero); + } + } + + // Left children + for (int i = 0; i < this.leftChildren.Count; i++) + { + if (canAdd) + { + this.leftChildren[i].Measure(infinity); + height = Math.Max(this.leftChildren[i].DesiredSize.Height, height); + if (width + this.leftChildren[i].DesiredSize.Width <= availableSize.Width) + { + width += this.leftChildren[i].DesiredSize.Width; + } + else + { + canAdd = false; + this.leftChildren[i].Measure(zero); + this.lastLeftIndex = i; + + } + } + else + { + this.leftChildren[i].Measure(zero); + } + } + + // Collapse other children + for (int i = 0; i < this.otherChildren.Count; i++) + { + this.otherChildren[i].Measure(zero); + } + + return new Size(width, height); + } + + /// + /// When overridden in a derived class, positions child elements and determines a size for a derived class. + /// + /// + /// The actual size used. + /// + /// The final area within the parent that this element should use to arrange itself and its children. + protected override Size ArrangeOverride(Size finalSize) + { + Rect zero = new Rect(0, 0, 0, 0); + + // Right shift + double rightShift = 0; + // Arrange right + for (int i = this.rightChildren.Count - 1; i >= 0; i--) + { + if (this.lastRightIndex > i) + { + rightShift += this.rightChildren[i].DesiredSize.Width; + this.rightChildren[i].Arrange(new Rect(finalSize.Width - rightShift, 0, this.rightChildren[i].DesiredSize.Width, finalSize.Height)); + } + else + this.rightChildren[i].Arrange(zero); + } + + // Left shift + double leftShift = 0; + // Arrange left + for (int i = 0; i < this.leftChildren.Count; i++) + { + if (i < this.lastLeftIndex) + { + this.leftChildren[i].Arrange(new Rect(leftShift, 0, this.leftChildren[i].DesiredSize.Width, finalSize.Height)); + leftShift += this.leftChildren[i].DesiredSize.Width; + } + else + this.leftChildren[i].Arrange(zero); + } + + // Arrange other + for (int i = 0; i < this.otherChildren.Count; i++) + { + this.otherChildren[i].Arrange(zero); + } + + return finalSize; + } + + #endregion + } +} diff --git a/Fluent/Controls/TextBox.cs b/Fluent.Ribbon/Controls/TextBox.cs similarity index 97% rename from Fluent/Controls/TextBox.cs rename to Fluent.Ribbon/Controls/TextBox.cs index aa87135d7..60b1b33c0 100644 --- a/Fluent/Controls/TextBox.cs +++ b/Fluent.Ribbon/Controls/TextBox.cs @@ -1,738 +1,738 @@ -using System; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Threading; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Controls.Primitives; -using System.Windows.Data; -using System.Windows.Input; -using System.Windows.Markup; -using System.Windows.Media; -using System.Windows.Threading; - -namespace Fluent -{ - /// - /// Represents custom Fluent UI TextBox - /// - [TemplatePart(Name = "PART_TextBox")] - [ContentProperty("Text")] - public class TextBox : RibbonControl - { - #region Events - - /// - /// Occurs when text is changed - /// - public event TextChangedEventHandler TextChanged; - - private void RaiseTextChanged(TextChangedEventArgs args) - { - if (this.TextChanged != null) - this.TextChanged(this, args); - } - - /// - /// Occurs when selection changed - /// - public event EventHandler SelectionChanged; - - private void RaiseSelectionChanged() - { - if (this.SelectionChanged != null) - this.SelectionChanged(this, EventArgs.Empty); - } - - #endregion - - #region Fields - - // TextBox in template - private System.Windows.Controls.TextBox textBoxTemplated; - // Local TextBox - private System.Windows.Controls.TextBox textBox = new System.Windows.Controls.TextBox(); - // Content when the textbox got focus - private string textBoxContentWhenGotFocus = null; - - #endregion - - #region Properties (Dependency) - - #region InputWidth - - /// - /// Gets or sets width of the value input part of textbox - /// - public double InputWidth - { - get { return (double)this.GetValue(InputWidthProperty); } - set { this.SetValue(InputWidthProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for InputWidth. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty InputWidthProperty = - DependencyProperty.Register("InputWidth", typeof(double), typeof(TextBox), new UIPropertyMetadata(double.NaN)); - - #endregion - - #region Text - - /// - /// Gets or sets text content of the textbox - /// - public string Text - { - get { return (string)this.GetValue(TextProperty); } - set { this.SetValue(TextProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Content. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty TextProperty = - DependencyProperty.Register("Text", typeof(string), typeof(TextBox), - new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, - null, null, true, UpdateSourceTrigger.LostFocus)); - - #endregion - - #region IsReadOnly - - /// - /// Gets or sets whether text can be edited. This is a dependency property. - /// - public bool IsReadOnly - { - get { return (bool)this.GetValue(IsReadOnlyProperty); } - set { this.SetValue(IsReadOnlyProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for IsReadonly. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IsReadOnlyProperty = - DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(TextBox), new UIPropertyMetadata(false)); - - #endregion - - #region CharacterCasing - - /// - /// Gets or sets how characters are cased - /// - public CharacterCasing CharacterCasing - { - get { return (CharacterCasing)this.GetValue(CharacterCasingProperty); } - set { this.SetValue(CharacterCasingProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for CharacterCasing. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty CharacterCasingProperty = - DependencyProperty.Register("CharacterCasing", typeof(CharacterCasing), typeof(TextBox), - new UIPropertyMetadata(CharacterCasing.Normal)); - - #endregion - - #region MaxLength - - /// - /// Gets or sets how many characters can be entered manually into the textbox - /// - public int MaxLength - { - get { return (int)this.GetValue(MaxLengthProperty); } - set { this.SetValue(MaxLengthProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for MaxLength. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty MaxLengthProperty = - DependencyProperty.Register("MaxLength", typeof(int), typeof(TextBox), new UIPropertyMetadata(int.MaxValue)); - - #endregion - - #region TextAlignment - - /// - /// Gets or sets horizontal text alignment of the content - /// - public TextAlignment TextAlignment - { - get { return (TextAlignment)this.GetValue(TextAlignmentProperty); } - set { this.SetValue(TextAlignmentProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for TextAlignment. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty TextAlignmentProperty = - DependencyProperty.Register("TextAlignment", typeof(TextAlignment), typeof(TextBox), - new UIPropertyMetadata(TextAlignment.Left)); - - #endregion - - #region TextDecorations - - /// - /// Gets or sets the text decorations to apply to the text box - /// - public TextDecorationCollection TextDecorations - { - get { return (TextDecorationCollection)this.GetValue(TextDecorationsProperty); } - set { this.SetValue(TextDecorationsProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for TextDecorations. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty TextDecorationsProperty = - DependencyProperty.Register("TextDecorations", typeof(TextDecorationCollection), typeof(TextBox), - new UIPropertyMetadata(new TextDecorationCollection())); - - #endregion - - #region IsUndoEnabled - - /// - /// Gets or sets a value that indicates whether undo support is enabled - /// - public bool IsUndoEnabled - { - get { return (bool)this.GetValue(IsUndoEnabledProperty); } - set { this.SetValue(IsUndoEnabledProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for IsUndoEnabled. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IsUndoEnabledProperty = - DependencyProperty.Register("IsUndoEnabled", typeof(bool), typeof(TextBox), - new UIPropertyMetadata(true)); - - #endregion - - #region UndoLimit - - /// - /// Gets or sets the number of actions stored in undo queue - /// - public int UndoLimit - { - get { return (int)this.GetValue(UndoLimitProperty); } - set { this.SetValue(UndoLimitProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for UndoLimit. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty UndoLimitProperty = - DependencyProperty.Register("UndoLimit", typeof(int), typeof(TextBox), new UIPropertyMetadata(1000)); - - #endregion - - #region AutoWordSelection - - /// - /// Gets or sets whether auto word selection feature is enabled - /// - public bool AutoWordSelection - { - get { return (bool)this.GetValue(AutoWordSelectionProperty); } - set { this.SetValue(AutoWordSelectionProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for AutoWordSelection. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty AutoWordSelectionProperty = - DependencyProperty.Register("AutoWordSelection", typeof(bool), typeof(TextBox), new UIPropertyMetadata(false)); - - #endregion - - #region SelectionBrush - - /// - /// Gets or sets the brush that highlights the selected text - /// - public Brush SelectionBrush - { - get { return (Brush)this.GetValue(SelectionBrushProperty); } - set { this.SetValue(SelectionBrushProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for SelectionBrush. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty SelectionBrushProperty = - DependencyProperty.Register("SelectionBrush", typeof(Brush), typeof(TextBox), - new UIPropertyMetadata(new SolidColorBrush(Color.FromArgb(0xFF, 0x33, 0x99, 0xFF)))); - - #endregion - - #region SelectionOpacity - - /// - /// Gets or sets opacity of the selection brush - /// - public double SelectionOpacity - { - get { return (double)this.GetValue(SelectionOpacityProperty); } - set { this.SetValue(SelectionOpacityProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for SelectionOpacity. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty SelectionOpacityProperty = - DependencyProperty.Register("SelectionOpacity", typeof(double), typeof(TextBox), new UIPropertyMetadata(0.4d)); - - #endregion - - #region CaretBrush - - /// - /// Gets or sets the caret brush that is used - /// to paint the caret of the text box - /// - public Brush CaretBrush - { - get { return (Brush)this.GetValue(CaretBrushProperty); } - set { this.SetValue(CaretBrushProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for CaretBrush. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty CaretBrushProperty = - DependencyProperty.Register("CaretBrush", typeof(Brush), typeof(TextBox), - new UIPropertyMetadata(null)); - - #endregion - - #endregion - - #region Properties - - /// - /// Gets or sets character index of the beginning of the current selection - /// - public int SelectionStart - { - get { return this.textBox.SelectionStart; } - set - { - if (this.textBoxTemplated != null) - this.textBoxTemplated.SelectionStart = value; - else - this.textBox.SelectionStart = value; - } - } - - /// - /// Gets or sets a value that indicating the number of characters in the current selection - /// - public int SelectionLength - { - get { return this.textBox.SelectionLength; } - set - { - if (this.textBoxTemplated != null) - this.textBoxTemplated.SelectionLength = value; - else - this.textBox.SelectionLength = value; - } - } - - /// - /// Gets or sets content of the selection - /// - public string SelectedText - { - get { return this.textBox.SelectedText; } - set - { - if (this.textBoxTemplated != null) - this.textBoxTemplated.SelectedText = value; - else - this.textBox.SelectedText = value; - } - } - - /// - /// Gets a value that indicating whether actions can be undo - /// - public bool CanUndo - { - get { return this.textBoxTemplated == null ? false : this.textBoxTemplated.CanUndo; } - } - - /// - /// Gets a value that indicating whether actions can be redo - /// - public bool CanRedo - { - get { return this.textBoxTemplated == null ? false : this.textBoxTemplated.CanRedo; } - } - - #endregion - - #region Constructors - - /// - /// Static constructor - /// - [SuppressMessage("Microsoft.Performance", "CA1810")] - static TextBox() - { - DefaultStyleKeyProperty.OverrideMetadata(typeof(TextBox), new FrameworkPropertyMetadata(typeof(TextBox))); - StyleProperty.OverrideMetadata(typeof(TextBox), new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceStyle))); - } - - // Coerce object style - private static object OnCoerceStyle(DependencyObject d, object basevalue) - { - if (basevalue == null) - { - basevalue = (d as FrameworkElement).TryFindResource(typeof(TextBox)); - } - - return basevalue; - } - - /// - /// Default constructor - /// - public TextBox() - { - this.Focusable = true; - this.textBox.SelectionChanged += (s, e) => this.RaiseSelectionChanged(); - this.textBox.TextChanged += (s, e) => this.RaiseTextChanged(e); - - Binding binding = new Binding("Text"); - binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged; - binding.Source = this; - binding.Mode = BindingMode.TwoWay; - this.textBox.SetBinding(System.Windows.Controls.TextBox.TextProperty, binding); - - Bind(this.textBox, this, "CharacterCasing", System.Windows.Controls.TextBox.CharacterCasingProperty, BindingMode.TwoWay); - Bind(this.textBox, this, "MaxLength", System.Windows.Controls.TextBox.MaxLengthProperty, BindingMode.TwoWay); - Bind(this.textBox, this, "TextAlignment", System.Windows.Controls.TextBox.TextAlignmentProperty, BindingMode.TwoWay); - Bind(this.textBox, this, "TextDecorations", System.Windows.Controls.TextBox.TextDecorationsProperty, BindingMode.TwoWay); - Bind(this.textBox, this, "IsUndoEnabled", TextBoxBase.IsUndoEnabledProperty, BindingMode.TwoWay); - Bind(this.textBox, this, "UndoLimit", TextBoxBase.UndoLimitProperty, BindingMode.TwoWay); - Bind(this.textBox, this, "AutoWordSelection", TextBoxBase.AutoWordSelectionProperty, BindingMode.TwoWay); - Bind(this.textBox, this, "SelectionBrush", TextBoxBase.SelectionBrushProperty, BindingMode.TwoWay); - Bind(this.textBox, this, "SelectionOpacity", TextBoxBase.SelectionOpacityProperty, BindingMode.TwoWay); - Bind(this.textBox, this, "CaretBrush", TextBoxBase.CaretBrushProperty, BindingMode.TwoWay); - Bind(this.textBox, this, "IsReadOnly", TextBoxBase.IsReadOnlyProperty, BindingMode.TwoWay); - } - - #endregion - - #region Methods - - /// - /// Appends text - /// - /// Text - public void AppendText(string text) - { - if (this.textBoxTemplated != null) - this.textBoxTemplated.AppendText(text); - else - this.textBox.AppendText(text); - } - - /// - /// Copies selected content to the clipboard - /// - public void Copy() - { - if (this.textBoxTemplated != null) - this.textBoxTemplated.Copy(); - else - this.textBox.Copy(); - } - - /// - /// Cuts selected content to the clipboard - /// - public void Cut() - { - if (this.textBoxTemplated != null) - this.textBoxTemplated.Cut(); - else - this.textBox.Cut(); - } - - /// - /// Pastes content from the clipboard - /// - public void Paste() - { - if (this.textBoxTemplated != null) - this.textBoxTemplated.Paste(); - else - this.textBox.Paste(); - } - - /// - /// Undoes the most recent undo command - /// - /// - public bool Undo() - { - if (this.textBoxTemplated != null) return this.textBoxTemplated.Undo(); - return false; - } - - /// - /// Redoes the most recent undo command - /// - /// - public bool Redo() - { - if (this.textBoxTemplated != null) return this.textBoxTemplated.Redo(); - return false; - } - - /// - /// Selects all the contents - /// - public void SelectAll() - { - if (this.textBoxTemplated != null) - this.textBoxTemplated.SelectAll(); - else - this.textBox.SelectAll(); - } - - /// - /// Selects contents - /// - /// Start - /// Length - public void Select(int start, int length) - { - if (this.textBoxTemplated != null) - this.textBoxTemplated.Select(start, length); - else - this.textBox.Select(start, length); - } - - #endregion - - #region Overrides - - /// - /// When overridden in a derived class, is invoked whenever - /// application code or internal processes call ApplyTemplate - /// - public override void OnApplyTemplate() - { - if (this.textBoxTemplated != null) - { - this.textBoxTemplated.PreviewKeyDown -= this.OnTextBoxTemplatedKeyDown; - this.textBoxTemplated.SelectionChanged -= this.OnTextBoxTemplatedSelectionChanged; - this.textBoxTemplated.LostFocus -= this.OnTextBoxTemplatedLostFocus; - this.textBoxTemplated.GotKeyboardFocus -= this.OnTextBoxTemplatedGotKeyboardFocus; - this.textBoxTemplated.TextChanged -= this.OnTextBoxTemplatedTextChanged; - BindingOperations.ClearAllBindings(this.textBoxTemplated); - } - this.textBoxTemplated = this.GetTemplateChild("PART_TextBox") as System.Windows.Controls.TextBox; - - - // Check template - if (!this.IsTemplateValid()) - { - Debug.WriteLine("Template for TextBox control is invalid"); - return; - } - - this.textBoxTemplated.Text = this.Text; - this.textBoxTemplated.Select(this.textBox.SelectionStart, this.textBox.SelectionLength); - - // Bindings - BindingOperations.ClearAllBindings(this.textBox); - - Binding binding = new Binding("Text"); - binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged; - binding.Source = this; - binding.Mode = BindingMode.TwoWay; - this.textBoxTemplated.SetBinding(System.Windows.Controls.TextBox.TextProperty, binding); - - Bind(this, this.textBoxTemplated, "CharacterCasing", System.Windows.Controls.TextBox.CharacterCasingProperty, BindingMode.TwoWay); - Bind(this, this.textBoxTemplated, "MaxLength", System.Windows.Controls.TextBox.MaxLengthProperty, BindingMode.TwoWay); - Bind(this, this.textBoxTemplated, "TextAlignment", System.Windows.Controls.TextBox.TextAlignmentProperty, BindingMode.TwoWay); - Bind(this, this.textBoxTemplated, "TextDecorations", System.Windows.Controls.TextBox.TextDecorationsProperty, BindingMode.TwoWay); - Bind(this, this.textBoxTemplated, "IsUndoEnabled", TextBoxBase.IsUndoEnabledProperty, BindingMode.TwoWay); - Bind(this, this.textBoxTemplated, "UndoLimit", TextBoxBase.UndoLimitProperty, BindingMode.TwoWay); - Bind(this, this.textBoxTemplated, "AutoWordSelection", TextBoxBase.AutoWordSelectionProperty, BindingMode.TwoWay); - Bind(this, this.textBoxTemplated, "SelectionBrush", TextBoxBase.SelectionBrushProperty, BindingMode.TwoWay); - Bind(this, this.textBoxTemplated, "SelectionOpacity", TextBoxBase.SelectionOpacityProperty, BindingMode.TwoWay); - Bind(this, this.textBoxTemplated, "CaretBrush", TextBoxBase.CaretBrushProperty, BindingMode.TwoWay); - Bind(this, this.textBoxTemplated, "IsReadOnly", TextBoxBase.IsReadOnlyProperty, BindingMode.TwoWay); - - this.textBoxTemplated.PreviewKeyDown += this.OnTextBoxTemplatedKeyDown; - this.textBoxTemplated.SelectionChanged += this.OnTextBoxTemplatedSelectionChanged; - this.textBoxTemplated.LostFocus += this.OnTextBoxTemplatedLostFocus; - this.textBoxTemplated.GotKeyboardFocus += this.OnTextBoxTemplatedGotKeyboardFocus; - this.textBoxTemplated.TextChanged += this.OnTextBoxTemplatedTextChanged; - } - - private void OnTextBoxTemplatedTextChanged(object sender, TextChangedEventArgs e) - { - this.RaiseTextChanged(e); - } - - private void OnTextBoxTemplatedGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e) - { - this.textBoxContentWhenGotFocus = this.textBoxTemplated.Text; - } - - private void OnTextBoxTemplatedLostFocus(object sender, RoutedEventArgs e) - { - if (this.textBoxContentWhenGotFocus != this.textBoxTemplated.Text) - this.ExecuteCommand(); - } - - private void OnTextBoxTemplatedSelectionChanged(object sender, RoutedEventArgs e) - { - this.textBox.Select(this.textBoxTemplated.SelectionStart, this.textBoxTemplated.SelectionLength); - } - - /// - /// Handles key tip pressed - /// - public override void OnKeyTipPressed() - { - if (!this.IsTemplateValid()) return; - - // Use dispatcher to avoid focus moving to backup'ed element - // (focused element before keytips processing) - this.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, - (ThreadStart)(() => - { - this.textBoxTemplated.SelectAll(); - this.textBoxTemplated.Focus(); - })); - } - - /// - /// Invoked whenever an unhandled event reaches this element in its route. - /// - /// The that contains the event data. - protected override void OnGotFocus(RoutedEventArgs e) - { - base.OnGotFocus(e); - - if (this.textBoxTemplated != null) - { - this.textBoxTemplated.Focus(); - } - } - - /// - /// Invoked when an unhandled System.Windows.Input.Keyboard.KeyUp�attached event reaches - /// an element in its route that is derived from this class. Implement this method to add class handling for this event. - /// - /// The System.Windows.Input.KeyEventArgs that contains the event data. - protected override void OnKeyUp(KeyEventArgs e) - { - // Avoid Click invocation (from RibbonControl) - if (e.Key == Key.Enter || e.Key == Key.Space) return; - base.OnKeyUp(e); - } - - private void OnTextBoxTemplatedKeyDown(object sender, KeyEventArgs e) - { - if (e.Key == Key.Enter) - { - // Move Focus - this.textBoxTemplated.Focusable = false; - this.Focus(); - this.textBoxTemplated.Focusable = true; - } - } - - #endregion - - #region Private methods - - private bool IsTemplateValid() - { - return this.textBoxTemplated != null; - } - - #endregion - - #region Quick Access Item Creating - - /// - /// Gets control which represents shortcut item. - /// This item MUST be syncronized with the original - /// and send command to original one control. - /// - /// Control which represents shortcut item - public override FrameworkElement CreateQuickAccessItem() - { - TextBox textBox = new TextBox(); - - this.BindQuickAccessItem(textBox); - - - return textBox; - } - - /// - /// This method must be overridden to bind properties to use in quick access creating - /// - /// Toolbar item - protected void BindQuickAccessItem(FrameworkElement element) - { - RibbonControl.BindQuickAccessItem(this, element); - - TextBox textBoxQAT = (TextBox)element; - - textBoxQAT.Width = this.Width; - textBoxQAT.InputWidth = this.InputWidth; - - Bind(this, textBoxQAT, "Text", TextProperty, BindingMode.TwoWay); - Bind(this, textBoxQAT, "IsReadOnly", IsReadOnlyProperty, BindingMode.OneWay); - Bind(this, textBoxQAT, "CharacterCasing", CharacterCasingProperty, BindingMode.TwoWay); - Bind(this, textBoxQAT, "MaxLength", MaxLengthProperty, BindingMode.TwoWay); - Bind(this, textBoxQAT, "TextAlignment", TextAlignmentProperty, BindingMode.TwoWay); - Bind(this, textBoxQAT, "TextDecorations", TextDecorationsProperty, BindingMode.TwoWay); - Bind(this, textBoxQAT, "IsUndoEnabled", IsUndoEnabledProperty, BindingMode.TwoWay); - Bind(this, textBoxQAT, "UndoLimit", UndoLimitProperty, BindingMode.TwoWay); - Bind(this, textBoxQAT, "AutoWordSelection", AutoWordSelectionProperty, BindingMode.TwoWay); - Bind(this, textBoxQAT, "SelectionBrush", SelectionBrushProperty, BindingMode.TwoWay); - Bind(this, textBoxQAT, "SelectionOpacity", SelectionOpacityProperty, BindingMode.TwoWay); - Bind(this, textBoxQAT, "CaretBrush", CaretBrushProperty, BindingMode.TwoWay); - Bind(this, textBoxQAT, "IsReadOnly", IsReadOnlyProperty, BindingMode.TwoWay); - - RibbonControl.BindQuickAccessItem(this, element); - } - - #endregion - } +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Data; +using System.Windows.Input; +using System.Windows.Markup; +using System.Windows.Media; +using System.Windows.Threading; + +namespace Fluent +{ + /// + /// Represents custom Fluent UI TextBox + /// + [TemplatePart(Name = "PART_TextBox")] + [ContentProperty("Text")] + public class TextBox : RibbonControl + { + #region Events + + /// + /// Occurs when text is changed + /// + public event TextChangedEventHandler TextChanged; + + private void RaiseTextChanged(TextChangedEventArgs args) + { + if (this.TextChanged != null) + this.TextChanged(this, args); + } + + /// + /// Occurs when selection changed + /// + public event EventHandler SelectionChanged; + + private void RaiseSelectionChanged() + { + if (this.SelectionChanged != null) + this.SelectionChanged(this, EventArgs.Empty); + } + + #endregion + + #region Fields + + // TextBox in template + private System.Windows.Controls.TextBox textBoxTemplated; + // Local TextBox + private System.Windows.Controls.TextBox textBox = new System.Windows.Controls.TextBox(); + // Content when the textbox got focus + private string textBoxContentWhenGotFocus = null; + + #endregion + + #region Properties (Dependency) + + #region InputWidth + + /// + /// Gets or sets width of the value input part of textbox + /// + public double InputWidth + { + get { return (double)this.GetValue(InputWidthProperty); } + set { this.SetValue(InputWidthProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for InputWidth. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty InputWidthProperty = + DependencyProperty.Register("InputWidth", typeof(double), typeof(TextBox), new UIPropertyMetadata(double.NaN)); + + #endregion + + #region Text + + /// + /// Gets or sets text content of the textbox + /// + public string Text + { + get { return (string)this.GetValue(TextProperty); } + set { this.SetValue(TextProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Content. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty TextProperty = + DependencyProperty.Register("Text", typeof(string), typeof(TextBox), + new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, + null, null, true, UpdateSourceTrigger.LostFocus)); + + #endregion + + #region IsReadOnly + + /// + /// Gets or sets whether text can be edited. This is a dependency property. + /// + public bool IsReadOnly + { + get { return (bool)this.GetValue(IsReadOnlyProperty); } + set { this.SetValue(IsReadOnlyProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for IsReadonly. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IsReadOnlyProperty = + DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(TextBox), new UIPropertyMetadata(false)); + + #endregion + + #region CharacterCasing + + /// + /// Gets or sets how characters are cased + /// + public CharacterCasing CharacterCasing + { + get { return (CharacterCasing)this.GetValue(CharacterCasingProperty); } + set { this.SetValue(CharacterCasingProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for CharacterCasing. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty CharacterCasingProperty = + DependencyProperty.Register("CharacterCasing", typeof(CharacterCasing), typeof(TextBox), + new UIPropertyMetadata(CharacterCasing.Normal)); + + #endregion + + #region MaxLength + + /// + /// Gets or sets how many characters can be entered manually into the textbox + /// + public int MaxLength + { + get { return (int)this.GetValue(MaxLengthProperty); } + set { this.SetValue(MaxLengthProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for MaxLength. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty MaxLengthProperty = + DependencyProperty.Register("MaxLength", typeof(int), typeof(TextBox), new UIPropertyMetadata(int.MaxValue)); + + #endregion + + #region TextAlignment + + /// + /// Gets or sets horizontal text alignment of the content + /// + public TextAlignment TextAlignment + { + get { return (TextAlignment)this.GetValue(TextAlignmentProperty); } + set { this.SetValue(TextAlignmentProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for TextAlignment. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty TextAlignmentProperty = + DependencyProperty.Register("TextAlignment", typeof(TextAlignment), typeof(TextBox), + new UIPropertyMetadata(TextAlignment.Left)); + + #endregion + + #region TextDecorations + + /// + /// Gets or sets the text decorations to apply to the text box + /// + public TextDecorationCollection TextDecorations + { + get { return (TextDecorationCollection)this.GetValue(TextDecorationsProperty); } + set { this.SetValue(TextDecorationsProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for TextDecorations. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty TextDecorationsProperty = + DependencyProperty.Register("TextDecorations", typeof(TextDecorationCollection), typeof(TextBox), + new UIPropertyMetadata(new TextDecorationCollection())); + + #endregion + + #region IsUndoEnabled + + /// + /// Gets or sets a value that indicates whether undo support is enabled + /// + public bool IsUndoEnabled + { + get { return (bool)this.GetValue(IsUndoEnabledProperty); } + set { this.SetValue(IsUndoEnabledProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for IsUndoEnabled. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IsUndoEnabledProperty = + DependencyProperty.Register("IsUndoEnabled", typeof(bool), typeof(TextBox), + new UIPropertyMetadata(true)); + + #endregion + + #region UndoLimit + + /// + /// Gets or sets the number of actions stored in undo queue + /// + public int UndoLimit + { + get { return (int)this.GetValue(UndoLimitProperty); } + set { this.SetValue(UndoLimitProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for UndoLimit. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty UndoLimitProperty = + DependencyProperty.Register("UndoLimit", typeof(int), typeof(TextBox), new UIPropertyMetadata(1000)); + + #endregion + + #region AutoWordSelection + + /// + /// Gets or sets whether auto word selection feature is enabled + /// + public bool AutoWordSelection + { + get { return (bool)this.GetValue(AutoWordSelectionProperty); } + set { this.SetValue(AutoWordSelectionProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for AutoWordSelection. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty AutoWordSelectionProperty = + DependencyProperty.Register("AutoWordSelection", typeof(bool), typeof(TextBox), new UIPropertyMetadata(false)); + + #endregion + + #region SelectionBrush + + /// + /// Gets or sets the brush that highlights the selected text + /// + public Brush SelectionBrush + { + get { return (Brush)this.GetValue(SelectionBrushProperty); } + set { this.SetValue(SelectionBrushProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for SelectionBrush. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty SelectionBrushProperty = + DependencyProperty.Register("SelectionBrush", typeof(Brush), typeof(TextBox), + new UIPropertyMetadata(new SolidColorBrush(Color.FromArgb(0xFF, 0x33, 0x99, 0xFF)))); + + #endregion + + #region SelectionOpacity + + /// + /// Gets or sets opacity of the selection brush + /// + public double SelectionOpacity + { + get { return (double)this.GetValue(SelectionOpacityProperty); } + set { this.SetValue(SelectionOpacityProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for SelectionOpacity. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty SelectionOpacityProperty = + DependencyProperty.Register("SelectionOpacity", typeof(double), typeof(TextBox), new UIPropertyMetadata(0.4d)); + + #endregion + + #region CaretBrush + + /// + /// Gets or sets the caret brush that is used + /// to paint the caret of the text box + /// + public Brush CaretBrush + { + get { return (Brush)this.GetValue(CaretBrushProperty); } + set { this.SetValue(CaretBrushProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for CaretBrush. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty CaretBrushProperty = + DependencyProperty.Register("CaretBrush", typeof(Brush), typeof(TextBox), + new UIPropertyMetadata(null)); + + #endregion + + #endregion + + #region Properties + + /// + /// Gets or sets character index of the beginning of the current selection + /// + public int SelectionStart + { + get { return this.textBox.SelectionStart; } + set + { + if (this.textBoxTemplated != null) + this.textBoxTemplated.SelectionStart = value; + else + this.textBox.SelectionStart = value; + } + } + + /// + /// Gets or sets a value that indicating the number of characters in the current selection + /// + public int SelectionLength + { + get { return this.textBox.SelectionLength; } + set + { + if (this.textBoxTemplated != null) + this.textBoxTemplated.SelectionLength = value; + else + this.textBox.SelectionLength = value; + } + } + + /// + /// Gets or sets content of the selection + /// + public string SelectedText + { + get { return this.textBox.SelectedText; } + set + { + if (this.textBoxTemplated != null) + this.textBoxTemplated.SelectedText = value; + else + this.textBox.SelectedText = value; + } + } + + /// + /// Gets a value that indicating whether actions can be undo + /// + public bool CanUndo + { + get { return this.textBoxTemplated == null ? false : this.textBoxTemplated.CanUndo; } + } + + /// + /// Gets a value that indicating whether actions can be redo + /// + public bool CanRedo + { + get { return this.textBoxTemplated == null ? false : this.textBoxTemplated.CanRedo; } + } + + #endregion + + #region Constructors + + /// + /// Static constructor + /// + [SuppressMessage("Microsoft.Performance", "CA1810")] + static TextBox() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(TextBox), new FrameworkPropertyMetadata(typeof(TextBox))); + StyleProperty.OverrideMetadata(typeof(TextBox), new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceStyle))); + } + + // Coerce object style + private static object OnCoerceStyle(DependencyObject d, object basevalue) + { + if (basevalue == null) + { + basevalue = (d as FrameworkElement).TryFindResource(typeof(TextBox)); + } + + return basevalue; + } + + /// + /// Default constructor + /// + public TextBox() + { + this.Focusable = true; + this.textBox.SelectionChanged += (s, e) => this.RaiseSelectionChanged(); + this.textBox.TextChanged += (s, e) => this.RaiseTextChanged(e); + + Binding binding = new Binding("Text"); + binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged; + binding.Source = this; + binding.Mode = BindingMode.TwoWay; + this.textBox.SetBinding(System.Windows.Controls.TextBox.TextProperty, binding); + + Bind(this.textBox, this, "CharacterCasing", System.Windows.Controls.TextBox.CharacterCasingProperty, BindingMode.TwoWay); + Bind(this.textBox, this, "MaxLength", System.Windows.Controls.TextBox.MaxLengthProperty, BindingMode.TwoWay); + Bind(this.textBox, this, "TextAlignment", System.Windows.Controls.TextBox.TextAlignmentProperty, BindingMode.TwoWay); + Bind(this.textBox, this, "TextDecorations", System.Windows.Controls.TextBox.TextDecorationsProperty, BindingMode.TwoWay); + Bind(this.textBox, this, "IsUndoEnabled", TextBoxBase.IsUndoEnabledProperty, BindingMode.TwoWay); + Bind(this.textBox, this, "UndoLimit", TextBoxBase.UndoLimitProperty, BindingMode.TwoWay); + Bind(this.textBox, this, "AutoWordSelection", TextBoxBase.AutoWordSelectionProperty, BindingMode.TwoWay); + Bind(this.textBox, this, "SelectionBrush", TextBoxBase.SelectionBrushProperty, BindingMode.TwoWay); + Bind(this.textBox, this, "SelectionOpacity", TextBoxBase.SelectionOpacityProperty, BindingMode.TwoWay); + Bind(this.textBox, this, "CaretBrush", TextBoxBase.CaretBrushProperty, BindingMode.TwoWay); + Bind(this.textBox, this, "IsReadOnly", TextBoxBase.IsReadOnlyProperty, BindingMode.TwoWay); + } + + #endregion + + #region Methods + + /// + /// Appends text + /// + /// Text + public void AppendText(string text) + { + if (this.textBoxTemplated != null) + this.textBoxTemplated.AppendText(text); + else + this.textBox.AppendText(text); + } + + /// + /// Copies selected content to the clipboard + /// + public void Copy() + { + if (this.textBoxTemplated != null) + this.textBoxTemplated.Copy(); + else + this.textBox.Copy(); + } + + /// + /// Cuts selected content to the clipboard + /// + public void Cut() + { + if (this.textBoxTemplated != null) + this.textBoxTemplated.Cut(); + else + this.textBox.Cut(); + } + + /// + /// Pastes content from the clipboard + /// + public void Paste() + { + if (this.textBoxTemplated != null) + this.textBoxTemplated.Paste(); + else + this.textBox.Paste(); + } + + /// + /// Undoes the most recent undo command + /// + /// + public bool Undo() + { + if (this.textBoxTemplated != null) return this.textBoxTemplated.Undo(); + return false; + } + + /// + /// Redoes the most recent undo command + /// + /// + public bool Redo() + { + if (this.textBoxTemplated != null) return this.textBoxTemplated.Redo(); + return false; + } + + /// + /// Selects all the contents + /// + public void SelectAll() + { + if (this.textBoxTemplated != null) + this.textBoxTemplated.SelectAll(); + else + this.textBox.SelectAll(); + } + + /// + /// Selects contents + /// + /// Start + /// Length + public void Select(int start, int length) + { + if (this.textBoxTemplated != null) + this.textBoxTemplated.Select(start, length); + else + this.textBox.Select(start, length); + } + + #endregion + + #region Overrides + + /// + /// When overridden in a derived class, is invoked whenever + /// application code or internal processes call ApplyTemplate + /// + public override void OnApplyTemplate() + { + if (this.textBoxTemplated != null) + { + this.textBoxTemplated.PreviewKeyDown -= this.OnTextBoxTemplatedKeyDown; + this.textBoxTemplated.SelectionChanged -= this.OnTextBoxTemplatedSelectionChanged; + this.textBoxTemplated.LostFocus -= this.OnTextBoxTemplatedLostFocus; + this.textBoxTemplated.GotKeyboardFocus -= this.OnTextBoxTemplatedGotKeyboardFocus; + this.textBoxTemplated.TextChanged -= this.OnTextBoxTemplatedTextChanged; + BindingOperations.ClearAllBindings(this.textBoxTemplated); + } + this.textBoxTemplated = this.GetTemplateChild("PART_TextBox") as System.Windows.Controls.TextBox; + + + // Check template + if (!this.IsTemplateValid()) + { + Debug.WriteLine("Template for TextBox control is invalid"); + return; + } + + this.textBoxTemplated.Text = this.Text; + this.textBoxTemplated.Select(this.textBox.SelectionStart, this.textBox.SelectionLength); + + // Bindings + BindingOperations.ClearAllBindings(this.textBox); + + Binding binding = new Binding("Text"); + binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged; + binding.Source = this; + binding.Mode = BindingMode.TwoWay; + this.textBoxTemplated.SetBinding(System.Windows.Controls.TextBox.TextProperty, binding); + + Bind(this, this.textBoxTemplated, "CharacterCasing", System.Windows.Controls.TextBox.CharacterCasingProperty, BindingMode.TwoWay); + Bind(this, this.textBoxTemplated, "MaxLength", System.Windows.Controls.TextBox.MaxLengthProperty, BindingMode.TwoWay); + Bind(this, this.textBoxTemplated, "TextAlignment", System.Windows.Controls.TextBox.TextAlignmentProperty, BindingMode.TwoWay); + Bind(this, this.textBoxTemplated, "TextDecorations", System.Windows.Controls.TextBox.TextDecorationsProperty, BindingMode.TwoWay); + Bind(this, this.textBoxTemplated, "IsUndoEnabled", TextBoxBase.IsUndoEnabledProperty, BindingMode.TwoWay); + Bind(this, this.textBoxTemplated, "UndoLimit", TextBoxBase.UndoLimitProperty, BindingMode.TwoWay); + Bind(this, this.textBoxTemplated, "AutoWordSelection", TextBoxBase.AutoWordSelectionProperty, BindingMode.TwoWay); + Bind(this, this.textBoxTemplated, "SelectionBrush", TextBoxBase.SelectionBrushProperty, BindingMode.TwoWay); + Bind(this, this.textBoxTemplated, "SelectionOpacity", TextBoxBase.SelectionOpacityProperty, BindingMode.TwoWay); + Bind(this, this.textBoxTemplated, "CaretBrush", TextBoxBase.CaretBrushProperty, BindingMode.TwoWay); + Bind(this, this.textBoxTemplated, "IsReadOnly", TextBoxBase.IsReadOnlyProperty, BindingMode.TwoWay); + + this.textBoxTemplated.PreviewKeyDown += this.OnTextBoxTemplatedKeyDown; + this.textBoxTemplated.SelectionChanged += this.OnTextBoxTemplatedSelectionChanged; + this.textBoxTemplated.LostFocus += this.OnTextBoxTemplatedLostFocus; + this.textBoxTemplated.GotKeyboardFocus += this.OnTextBoxTemplatedGotKeyboardFocus; + this.textBoxTemplated.TextChanged += this.OnTextBoxTemplatedTextChanged; + } + + private void OnTextBoxTemplatedTextChanged(object sender, TextChangedEventArgs e) + { + this.RaiseTextChanged(e); + } + + private void OnTextBoxTemplatedGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e) + { + this.textBoxContentWhenGotFocus = this.textBoxTemplated.Text; + } + + private void OnTextBoxTemplatedLostFocus(object sender, RoutedEventArgs e) + { + if (this.textBoxContentWhenGotFocus != this.textBoxTemplated.Text) + this.ExecuteCommand(); + } + + private void OnTextBoxTemplatedSelectionChanged(object sender, RoutedEventArgs e) + { + this.textBox.Select(this.textBoxTemplated.SelectionStart, this.textBoxTemplated.SelectionLength); + } + + /// + /// Handles key tip pressed + /// + public override void OnKeyTipPressed() + { + if (!this.IsTemplateValid()) return; + + // Use dispatcher to avoid focus moving to backup'ed element + // (focused element before keytips processing) + this.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, + (ThreadStart)(() => + { + this.textBoxTemplated.SelectAll(); + this.textBoxTemplated.Focus(); + })); + } + + /// + /// Invoked whenever an unhandled event reaches this element in its route. + /// + /// The that contains the event data. + protected override void OnGotFocus(RoutedEventArgs e) + { + base.OnGotFocus(e); + + if (this.textBoxTemplated != null) + { + this.textBoxTemplated.Focus(); + } + } + + /// + /// Invoked when an unhandled System.Windows.Input.Keyboard.KeyUp�attached event reaches + /// an element in its route that is derived from this class. Implement this method to add class handling for this event. + /// + /// The System.Windows.Input.KeyEventArgs that contains the event data. + protected override void OnKeyUp(KeyEventArgs e) + { + // Avoid Click invocation (from RibbonControl) + if (e.Key == Key.Enter || e.Key == Key.Space) return; + base.OnKeyUp(e); + } + + private void OnTextBoxTemplatedKeyDown(object sender, KeyEventArgs e) + { + if (e.Key == Key.Enter) + { + // Move Focus + this.textBoxTemplated.Focusable = false; + this.Focus(); + this.textBoxTemplated.Focusable = true; + } + } + + #endregion + + #region Private methods + + private bool IsTemplateValid() + { + return this.textBoxTemplated != null; + } + + #endregion + + #region Quick Access Item Creating + + /// + /// Gets control which represents shortcut item. + /// This item MUST be syncronized with the original + /// and send command to original one control. + /// + /// Control which represents shortcut item + public override FrameworkElement CreateQuickAccessItem() + { + TextBox textBox = new TextBox(); + + this.BindQuickAccessItem(textBox); + + + return textBox; + } + + /// + /// This method must be overridden to bind properties to use in quick access creating + /// + /// Toolbar item + protected void BindQuickAccessItem(FrameworkElement element) + { + RibbonControl.BindQuickAccessItem(this, element); + + TextBox textBoxQAT = (TextBox)element; + + textBoxQAT.Width = this.Width; + textBoxQAT.InputWidth = this.InputWidth; + + Bind(this, textBoxQAT, "Text", TextProperty, BindingMode.TwoWay); + Bind(this, textBoxQAT, "IsReadOnly", IsReadOnlyProperty, BindingMode.OneWay); + Bind(this, textBoxQAT, "CharacterCasing", CharacterCasingProperty, BindingMode.TwoWay); + Bind(this, textBoxQAT, "MaxLength", MaxLengthProperty, BindingMode.TwoWay); + Bind(this, textBoxQAT, "TextAlignment", TextAlignmentProperty, BindingMode.TwoWay); + Bind(this, textBoxQAT, "TextDecorations", TextDecorationsProperty, BindingMode.TwoWay); + Bind(this, textBoxQAT, "IsUndoEnabled", IsUndoEnabledProperty, BindingMode.TwoWay); + Bind(this, textBoxQAT, "UndoLimit", UndoLimitProperty, BindingMode.TwoWay); + Bind(this, textBoxQAT, "AutoWordSelection", AutoWordSelectionProperty, BindingMode.TwoWay); + Bind(this, textBoxQAT, "SelectionBrush", SelectionBrushProperty, BindingMode.TwoWay); + Bind(this, textBoxQAT, "SelectionOpacity", SelectionOpacityProperty, BindingMode.TwoWay); + Bind(this, textBoxQAT, "CaretBrush", CaretBrushProperty, BindingMode.TwoWay); + Bind(this, textBoxQAT, "IsReadOnly", IsReadOnlyProperty, BindingMode.TwoWay); + + RibbonControl.BindQuickAccessItem(this, element); + } + + #endregion + } } \ No newline at end of file diff --git a/Fluent/Controls/ToggleButton.cs b/Fluent.Ribbon/Controls/ToggleButton.cs similarity index 97% rename from Fluent/Controls/ToggleButton.cs rename to Fluent.Ribbon/Controls/ToggleButton.cs index 99a6e3e66..16c229703 100644 --- a/Fluent/Controls/ToggleButton.cs +++ b/Fluent.Ribbon/Controls/ToggleButton.cs @@ -1,316 +1,316 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.Windows; -using System.Windows.Data; -using System.Windows.Markup; - -namespace Fluent -{ - /// - /// Represents toggle button - /// - [ContentProperty("Header")] - public class ToggleButton : System.Windows.Controls.Primitives.ToggleButton, IToggleButton, IRibbonControl, IQuickAccessItemProvider - { - #region Properties - - #region Size - - /// - /// Gets or sets Size for the element. - /// - public RibbonControlSize Size - { - get { return (RibbonControlSize)this.GetValue(SizeProperty); } - set { this.SetValue(SizeProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Size. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty SizeProperty = RibbonProperties.SizeProperty.AddOwner(typeof(ToggleButton)); - - #endregion - - #region SizeDefinition - - /// - /// Gets or sets SizeDefinition for element. - /// - public RibbonControlSizeDefinition SizeDefinition - { - get { return (RibbonControlSizeDefinition)this.GetValue(SizeDefinitionProperty); } - set { this.SetValue(SizeDefinitionProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for SizeDefinition. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty SizeDefinitionProperty = RibbonProperties.SizeDefinitionProperty.AddOwner(typeof(ToggleButton)); - - #endregion - - #region KeyTip - - /// - /// Gets or sets KeyTip for element. - /// - public string KeyTip - { - get { return (string)this.GetValue(KeyTipProperty); } - set { this.SetValue(KeyTipProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Keys. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty KeyTipProperty = Fluent.KeyTip.KeysProperty.AddOwner(typeof(ToggleButton)); - - #endregion - - #region GroupName - - /// - /// Gets or sets the name of the group that the toggle button belongs to. - /// Use the GroupName property to specify a grouping of toggle buttons to - /// create a mutually exclusive set of controls. You can use the GroupName - /// property when only one selection is possible from a list of available - /// options. When this property is set, only one ToggleButton in the specified - /// group can be selected at a time. - /// - public string GroupName - { - get { return (string)this.GetValue(GroupNameProperty); } - set { this.SetValue(GroupNameProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for GroupName. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty GroupNameProperty = - DependencyProperty.Register("GroupName", typeof(string), typeof(ToggleButton), - new UIPropertyMetadata(null, ToggleButtonHelper.OnGroupNameChanged)); - - #endregion - - #region Header - - /// - /// Gets or sets element Text - /// - public object Header - { - get { return (string)this.GetValue(HeaderProperty); } - set { this.SetValue(HeaderProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Header. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty HeaderProperty = RibbonControl.HeaderProperty.AddOwner(typeof(ToggleButton)); - - #endregion - - #region Icon - - /// - /// Gets or sets Icon for the element - /// - public object Icon - { - get { return this.GetValue(IconProperty); } - set { this.SetValue(IconProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Icon. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IconProperty = RibbonControl.IconProperty.AddOwner(typeof(ToggleButton), new UIPropertyMetadata(null, OnIconChanged)); - - private static void OnIconChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var element = d as ToggleButton; - - var oldElement = e.OldValue as FrameworkElement; - - if (oldElement != null) - { - element.RemoveLogicalChild(oldElement); - } - - var newElement = e.NewValue as FrameworkElement; - - if (newElement != null - && LogicalTreeHelper.GetParent(newElement) == null) - { - element.AddLogicalChild(newElement); - } - } - - #endregion - - #region LargeIcon - - /// - /// Gets or sets button large icon - /// - public object LargeIcon - { - get { return this.GetValue(LargeIconProperty); } - set { this.SetValue(LargeIconProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for SmallIcon. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty LargeIconProperty = - DependencyProperty.Register("LargeIcon", typeof(object), - typeof(ToggleButton), new UIPropertyMetadata(null)); - - #endregion - - #region IsDefinitive - - /// - /// Gets or sets whether ribbon control click must close backstage - /// - public bool IsDefinitive - { - get { return (bool)this.GetValue(IsDefinitiveProperty); } - set { this.SetValue(IsDefinitiveProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for IsDefinitive. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IsDefinitiveProperty = - DependencyProperty.Register("IsDefinitive", typeof(bool), typeof(ToggleButton), new UIPropertyMetadata(true)); - - #endregion - - #endregion - - #region Constructors - - /// - /// Static constructor - /// - [SuppressMessage("Microsoft.Performance", "CA1810")] - static ToggleButton() - { - Type type = typeof(ToggleButton); - DefaultStyleKeyProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(type)); - IsCheckedProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(ToggleButtonHelper.OnIsCheckedChanged, ToggleButtonHelper.CoerceIsChecked)); - ContextMenuService.Attach(type); - ToolTipService.Attach(type); - StyleProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceStyle))); - } - - // Coerce object style - static object OnCoerceStyle(DependencyObject d, object basevalue) - { - if (basevalue == null) - { - basevalue = (d as FrameworkElement).TryFindResource(typeof(ToggleButton)); - } - - return basevalue; - } - - /// - /// Default constructor - /// - public ToggleButton() - { - ContextMenuService.Coerce(this); - } - - #endregion - - #region Overrides - - /// - /// Called when a is clicked. - /// - protected override void OnClick() - { - // Close popup on click - if (this.IsDefinitive) - { - PopupService.RaiseDismissPopupEvent(this, DismissPopupMode.Always); - } - - base.OnClick(); - } - - #endregion - - /// - /// Used to call OnClick (which is protected) - /// - public void InvokeClick() - { - this.OnClick(); - } - - #region Quick Access Item Creating - - /// - /// Gets control which represents shortcut item. - /// This item MUST be syncronized with the original - /// and send command to original one control. - /// - /// Control which represents shortcut item - public virtual FrameworkElement CreateQuickAccessItem() - { - ToggleButton button = new ToggleButton(); - - RibbonControl.Bind(this, button, "IsChecked", IsCheckedProperty, BindingMode.TwoWay); - button.Click += ((sender, e) => this.RaiseEvent(e)); - RibbonControl.BindQuickAccessItem(this, button); - - return button; - } - - /// - /// Gets or sets whether control can be added to quick access toolbar - /// - public bool CanAddToQuickAccessToolBar - { - get { return (bool)this.GetValue(CanAddToQuickAccessToolBarProperty); } - set { this.SetValue(CanAddToQuickAccessToolBarProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for CanAddToQuickAccessToolBar. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty CanAddToQuickAccessToolBarProperty = RibbonControl.CanAddToQuickAccessToolBarProperty.AddOwner(typeof(ToggleButton), new UIPropertyMetadata(true, RibbonControl.OnCanAddToQuickAccessToolbarChanged)); - - #endregion - - #region Implementation of IKeyTipedControl - - /// - /// Handles key tip pressed - /// - public void OnKeyTipPressed() - { - this.OnClick(); - } - - /// - /// Handles back navigation with KeyTips - /// - public void OnKeyTipBack() - { - } - - #endregion - } -} +using System; +using System.Diagnostics.CodeAnalysis; +using System.Windows; +using System.Windows.Data; +using System.Windows.Markup; + +namespace Fluent +{ + /// + /// Represents toggle button + /// + [ContentProperty("Header")] + public class ToggleButton : System.Windows.Controls.Primitives.ToggleButton, IToggleButton, IRibbonControl, IQuickAccessItemProvider + { + #region Properties + + #region Size + + /// + /// Gets or sets Size for the element. + /// + public RibbonControlSize Size + { + get { return (RibbonControlSize)this.GetValue(SizeProperty); } + set { this.SetValue(SizeProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Size. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty SizeProperty = RibbonProperties.SizeProperty.AddOwner(typeof(ToggleButton)); + + #endregion + + #region SizeDefinition + + /// + /// Gets or sets SizeDefinition for element. + /// + public RibbonControlSizeDefinition SizeDefinition + { + get { return (RibbonControlSizeDefinition)this.GetValue(SizeDefinitionProperty); } + set { this.SetValue(SizeDefinitionProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for SizeDefinition. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty SizeDefinitionProperty = RibbonProperties.SizeDefinitionProperty.AddOwner(typeof(ToggleButton)); + + #endregion + + #region KeyTip + + /// + /// Gets or sets KeyTip for element. + /// + public string KeyTip + { + get { return (string)this.GetValue(KeyTipProperty); } + set { this.SetValue(KeyTipProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Keys. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty KeyTipProperty = Fluent.KeyTip.KeysProperty.AddOwner(typeof(ToggleButton)); + + #endregion + + #region GroupName + + /// + /// Gets or sets the name of the group that the toggle button belongs to. + /// Use the GroupName property to specify a grouping of toggle buttons to + /// create a mutually exclusive set of controls. You can use the GroupName + /// property when only one selection is possible from a list of available + /// options. When this property is set, only one ToggleButton in the specified + /// group can be selected at a time. + /// + public string GroupName + { + get { return (string)this.GetValue(GroupNameProperty); } + set { this.SetValue(GroupNameProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for GroupName. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty GroupNameProperty = + DependencyProperty.Register("GroupName", typeof(string), typeof(ToggleButton), + new UIPropertyMetadata(null, ToggleButtonHelper.OnGroupNameChanged)); + + #endregion + + #region Header + + /// + /// Gets or sets element Text + /// + public object Header + { + get { return (string)this.GetValue(HeaderProperty); } + set { this.SetValue(HeaderProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Header. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty HeaderProperty = RibbonControl.HeaderProperty.AddOwner(typeof(ToggleButton)); + + #endregion + + #region Icon + + /// + /// Gets or sets Icon for the element + /// + public object Icon + { + get { return this.GetValue(IconProperty); } + set { this.SetValue(IconProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Icon. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IconProperty = RibbonControl.IconProperty.AddOwner(typeof(ToggleButton), new UIPropertyMetadata(null, OnIconChanged)); + + private static void OnIconChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var element = d as ToggleButton; + + var oldElement = e.OldValue as FrameworkElement; + + if (oldElement != null) + { + element.RemoveLogicalChild(oldElement); + } + + var newElement = e.NewValue as FrameworkElement; + + if (newElement != null + && LogicalTreeHelper.GetParent(newElement) == null) + { + element.AddLogicalChild(newElement); + } + } + + #endregion + + #region LargeIcon + + /// + /// Gets or sets button large icon + /// + public object LargeIcon + { + get { return this.GetValue(LargeIconProperty); } + set { this.SetValue(LargeIconProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for SmallIcon. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty LargeIconProperty = + DependencyProperty.Register("LargeIcon", typeof(object), + typeof(ToggleButton), new UIPropertyMetadata(null)); + + #endregion + + #region IsDefinitive + + /// + /// Gets or sets whether ribbon control click must close backstage + /// + public bool IsDefinitive + { + get { return (bool)this.GetValue(IsDefinitiveProperty); } + set { this.SetValue(IsDefinitiveProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for IsDefinitive. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IsDefinitiveProperty = + DependencyProperty.Register("IsDefinitive", typeof(bool), typeof(ToggleButton), new UIPropertyMetadata(true)); + + #endregion + + #endregion + + #region Constructors + + /// + /// Static constructor + /// + [SuppressMessage("Microsoft.Performance", "CA1810")] + static ToggleButton() + { + Type type = typeof(ToggleButton); + DefaultStyleKeyProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(type)); + IsCheckedProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(ToggleButtonHelper.OnIsCheckedChanged, ToggleButtonHelper.CoerceIsChecked)); + ContextMenuService.Attach(type); + ToolTipService.Attach(type); + StyleProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceStyle))); + } + + // Coerce object style + static object OnCoerceStyle(DependencyObject d, object basevalue) + { + if (basevalue == null) + { + basevalue = (d as FrameworkElement).TryFindResource(typeof(ToggleButton)); + } + + return basevalue; + } + + /// + /// Default constructor + /// + public ToggleButton() + { + ContextMenuService.Coerce(this); + } + + #endregion + + #region Overrides + + /// + /// Called when a is clicked. + /// + protected override void OnClick() + { + // Close popup on click + if (this.IsDefinitive) + { + PopupService.RaiseDismissPopupEvent(this, DismissPopupMode.Always); + } + + base.OnClick(); + } + + #endregion + + /// + /// Used to call OnClick (which is protected) + /// + public void InvokeClick() + { + this.OnClick(); + } + + #region Quick Access Item Creating + + /// + /// Gets control which represents shortcut item. + /// This item MUST be syncronized with the original + /// and send command to original one control. + /// + /// Control which represents shortcut item + public virtual FrameworkElement CreateQuickAccessItem() + { + ToggleButton button = new ToggleButton(); + + RibbonControl.Bind(this, button, "IsChecked", IsCheckedProperty, BindingMode.TwoWay); + button.Click += ((sender, e) => this.RaiseEvent(e)); + RibbonControl.BindQuickAccessItem(this, button); + + return button; + } + + /// + /// Gets or sets whether control can be added to quick access toolbar + /// + public bool CanAddToQuickAccessToolBar + { + get { return (bool)this.GetValue(CanAddToQuickAccessToolBarProperty); } + set { this.SetValue(CanAddToQuickAccessToolBarProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for CanAddToQuickAccessToolBar. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty CanAddToQuickAccessToolBarProperty = RibbonControl.CanAddToQuickAccessToolBarProperty.AddOwner(typeof(ToggleButton), new UIPropertyMetadata(true, RibbonControl.OnCanAddToQuickAccessToolbarChanged)); + + #endregion + + #region Implementation of IKeyTipedControl + + /// + /// Handles key tip pressed + /// + public void OnKeyTipPressed() + { + this.OnClick(); + } + + /// + /// Handles back navigation with KeyTips + /// + public void OnKeyTipBack() + { + } + + #endregion + } +} diff --git a/Fluent/Controls/TwoLineLabel.cs b/Fluent.Ribbon/Controls/TwoLineLabel.cs similarity index 97% rename from Fluent/Controls/TwoLineLabel.cs rename to Fluent.Ribbon/Controls/TwoLineLabel.cs index 90307b12e..b93bb0810 100644 --- a/Fluent/Controls/TwoLineLabel.cs +++ b/Fluent.Ribbon/Controls/TwoLineLabel.cs @@ -1,226 +1,226 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Documents; - -namespace Fluent -{ - /// - /// Represents specific label to use in particular ribbon controls - /// - [TemplatePart(Name = "PART_TextRun", Type = typeof(TextBlock))] - [TemplatePart(Name = "PART_TextRun2", Type = typeof(TextBlock))] - [TemplatePart(Name = "PART_Glyph", Type = typeof(InlineUIContainer))] - public class TwoLineLabel: Control - { - #region Fields - - /// - /// Run with text - /// - private AccessText textRun; - - private AccessText textRun2; - - #endregion - - #region Properties - - /// - /// Gets or sets whether label must have two lines - /// - public bool HasTwoLines - { - get { return (bool)this.GetValue(HasTwoLinesProperty); } - set { this.SetValue(HasTwoLinesProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for HasTwoLines. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty HasTwoLinesProperty = - DependencyProperty.Register("HasTwoLines", typeof(bool), typeof(TwoLineLabel), new UIPropertyMetadata(true,OnHasTwoLinesChanged)); - - /// - /// Handles HasTwoLines property changes - /// - /// Object - /// The event data - private static void OnHasTwoLinesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - (d as TwoLineLabel).UpdateTextRun(); - } - - /// - /// Gets or sets whether label has glyph - /// - public bool HasGlyph - { - get { return (bool)this.GetValue(HasGlyphProperty); } - set { this.SetValue(HasGlyphProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for HasGlyph. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty HasGlyphProperty = - DependencyProperty.Register("HasGlyph", typeof(bool), typeof(TwoLineLabel), new UIPropertyMetadata(false, OnHasGlyphChanged)); - - /// - /// Handles HasGlyph property changes - /// - /// Object - /// The event data - private static void OnHasGlyphChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - (d as TwoLineLabel).UpdateTextRun(); - } - - /// - /// Gets or sets labels text - /// - public string Text - { - get { return (string)this.GetValue(TextProperty); } - set { this.SetValue(TextProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for Text. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty TextProperty = - DependencyProperty.Register("Text", typeof(string), typeof(TwoLineLabel), new UIPropertyMetadata("TwoLineLabel", OnTextChanged)); - - #endregion - - #region Initialize - - /// - /// Static constructor - /// - [SuppressMessage("Microsoft.Performance", "CA1810")] - static TwoLineLabel() - { - DefaultStyleKeyProperty.OverrideMetadata(typeof(TwoLineLabel), new FrameworkPropertyMetadata(typeof(TwoLineLabel))); - StyleProperty.OverrideMetadata(typeof(TwoLineLabel), new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceStyle))); - } - - // Coerce object style - static object OnCoerceStyle(DependencyObject d, object basevalue) - { - if (basevalue == null) - { - basevalue = (d as FrameworkElement).TryFindResource(typeof(TwoLineLabel)); - } - - return basevalue; - } - - /// - /// Default constructor - /// - public TwoLineLabel() - { - this.Focusable = false; - } - - #endregion - - #region Overrides - - /// - /// When overridden in a derived class, is invoked whenever application code or internal - /// processes call System.Windows.FrameworkElement.ApplyTemplate(). - /// - public override void OnApplyTemplate() - { - this.textRun = this.GetTemplateChild("PART_TextRun") as AccessText; - this.textRun2 = this.GetTemplateChild("PART_TextRun2") as AccessText; - this.UpdateTextRun(); - } - - #endregion - - #region Event handling - - /// - /// Handles text property changes - /// - /// Object - /// The event data - private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - TwoLineLabel label = d as TwoLineLabel; - label.UpdateTextRun(); - } - - #endregion - - #region Private methods - - /// - /// Updates text run adds newline if HasTwoLines == true - /// - void UpdateTextRun() - { - if ((this.textRun != null)&&(this.textRun2 != null)&&(this.Text != null)) - { - this.textRun.Text = this.Text; - this.textRun2.Text = ""; - string text = this.Text.Trim(); - if (this.HasTwoLines) - { - // Find soft hyphen, break at its position and display a normal hyphen. - int hyphenIndex = text.IndexOf((char)173); - if (hyphenIndex >= 0) - { - this.textRun.Text = text.Substring(0, hyphenIndex) + "-"; - this.textRun2.Text = text.Substring(hyphenIndex) + " "; - } - else - { - int centerIndex = this.Text.Length / 2; - // Find spaces nearest to center from left and right - int leftSpaceIndex = text.LastIndexOf(" ", centerIndex, centerIndex); - int rightSpaceIndex = text.IndexOf(" ", centerIndex, StringComparison.CurrentCulture); - if ((leftSpaceIndex == -1) && (rightSpaceIndex == -1)) - { - // The text can`t be separated. Add new line for glyph - //textRun.Text += '\u0085'; - } - else if (leftSpaceIndex == -1) - { - // Finds only space from right. New line adds on it - this.textRun.Text = text.Substring(0, rightSpaceIndex); - this.textRun2.Text = text.Substring(rightSpaceIndex) + " "; - } - else if (rightSpaceIndex == -1) - { - // Finds only space from left. New line adds on it - this.textRun.Text = text.Substring(0, leftSpaceIndex); - this.textRun2.Text = text.Substring(leftSpaceIndex) + " "; - } - else - { - // Find nearest to center space and add new line on it - if (Math.Abs(centerIndex - leftSpaceIndex) < Math.Abs(centerIndex - rightSpaceIndex)) - { - this.textRun.Text = text.Substring(0, leftSpaceIndex); - this.textRun2.Text = text.Substring(leftSpaceIndex) + " "; - } - else - { - this.textRun.Text = text.Substring(0, rightSpaceIndex); - this.textRun2.Text = text.Substring(rightSpaceIndex) + " "; - } - } - } - } - } - } - - #endregion - } -} +using System; +using System.Diagnostics.CodeAnalysis; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; + +namespace Fluent +{ + /// + /// Represents specific label to use in particular ribbon controls + /// + [TemplatePart(Name = "PART_TextRun", Type = typeof(TextBlock))] + [TemplatePart(Name = "PART_TextRun2", Type = typeof(TextBlock))] + [TemplatePart(Name = "PART_Glyph", Type = typeof(InlineUIContainer))] + public class TwoLineLabel: Control + { + #region Fields + + /// + /// Run with text + /// + private AccessText textRun; + + private AccessText textRun2; + + #endregion + + #region Properties + + /// + /// Gets or sets whether label must have two lines + /// + public bool HasTwoLines + { + get { return (bool)this.GetValue(HasTwoLinesProperty); } + set { this.SetValue(HasTwoLinesProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for HasTwoLines. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty HasTwoLinesProperty = + DependencyProperty.Register("HasTwoLines", typeof(bool), typeof(TwoLineLabel), new UIPropertyMetadata(true,OnHasTwoLinesChanged)); + + /// + /// Handles HasTwoLines property changes + /// + /// Object + /// The event data + private static void OnHasTwoLinesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + (d as TwoLineLabel).UpdateTextRun(); + } + + /// + /// Gets or sets whether label has glyph + /// + public bool HasGlyph + { + get { return (bool)this.GetValue(HasGlyphProperty); } + set { this.SetValue(HasGlyphProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for HasGlyph. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty HasGlyphProperty = + DependencyProperty.Register("HasGlyph", typeof(bool), typeof(TwoLineLabel), new UIPropertyMetadata(false, OnHasGlyphChanged)); + + /// + /// Handles HasGlyph property changes + /// + /// Object + /// The event data + private static void OnHasGlyphChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + (d as TwoLineLabel).UpdateTextRun(); + } + + /// + /// Gets or sets labels text + /// + public string Text + { + get { return (string)this.GetValue(TextProperty); } + set { this.SetValue(TextProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for Text. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty TextProperty = + DependencyProperty.Register("Text", typeof(string), typeof(TwoLineLabel), new UIPropertyMetadata("TwoLineLabel", OnTextChanged)); + + #endregion + + #region Initialize + + /// + /// Static constructor + /// + [SuppressMessage("Microsoft.Performance", "CA1810")] + static TwoLineLabel() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(TwoLineLabel), new FrameworkPropertyMetadata(typeof(TwoLineLabel))); + StyleProperty.OverrideMetadata(typeof(TwoLineLabel), new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceStyle))); + } + + // Coerce object style + static object OnCoerceStyle(DependencyObject d, object basevalue) + { + if (basevalue == null) + { + basevalue = (d as FrameworkElement).TryFindResource(typeof(TwoLineLabel)); + } + + return basevalue; + } + + /// + /// Default constructor + /// + public TwoLineLabel() + { + this.Focusable = false; + } + + #endregion + + #region Overrides + + /// + /// When overridden in a derived class, is invoked whenever application code or internal + /// processes call System.Windows.FrameworkElement.ApplyTemplate(). + /// + public override void OnApplyTemplate() + { + this.textRun = this.GetTemplateChild("PART_TextRun") as AccessText; + this.textRun2 = this.GetTemplateChild("PART_TextRun2") as AccessText; + this.UpdateTextRun(); + } + + #endregion + + #region Event handling + + /// + /// Handles text property changes + /// + /// Object + /// The event data + private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + TwoLineLabel label = d as TwoLineLabel; + label.UpdateTextRun(); + } + + #endregion + + #region Private methods + + /// + /// Updates text run adds newline if HasTwoLines == true + /// + void UpdateTextRun() + { + if ((this.textRun != null)&&(this.textRun2 != null)&&(this.Text != null)) + { + this.textRun.Text = this.Text; + this.textRun2.Text = ""; + string text = this.Text.Trim(); + if (this.HasTwoLines) + { + // Find soft hyphen, break at its position and display a normal hyphen. + int hyphenIndex = text.IndexOf((char)173); + if (hyphenIndex >= 0) + { + this.textRun.Text = text.Substring(0, hyphenIndex) + "-"; + this.textRun2.Text = text.Substring(hyphenIndex) + " "; + } + else + { + int centerIndex = this.Text.Length / 2; + // Find spaces nearest to center from left and right + int leftSpaceIndex = text.LastIndexOf(" ", centerIndex, centerIndex); + int rightSpaceIndex = text.IndexOf(" ", centerIndex, StringComparison.CurrentCulture); + if ((leftSpaceIndex == -1) && (rightSpaceIndex == -1)) + { + // The text can`t be separated. Add new line for glyph + //textRun.Text += '\u0085'; + } + else if (leftSpaceIndex == -1) + { + // Finds only space from right. New line adds on it + this.textRun.Text = text.Substring(0, rightSpaceIndex); + this.textRun2.Text = text.Substring(rightSpaceIndex) + " "; + } + else if (rightSpaceIndex == -1) + { + // Finds only space from left. New line adds on it + this.textRun.Text = text.Substring(0, leftSpaceIndex); + this.textRun2.Text = text.Substring(leftSpaceIndex) + " "; + } + else + { + // Find nearest to center space and add new line on it + if (Math.Abs(centerIndex - leftSpaceIndex) < Math.Abs(centerIndex - rightSpaceIndex)) + { + this.textRun.Text = text.Substring(0, leftSpaceIndex); + this.textRun2.Text = text.Substring(leftSpaceIndex) + " "; + } + else + { + this.textRun.Text = text.Substring(0, rightSpaceIndex); + this.textRun2.Text = text.Substring(rightSpaceIndex) + " "; + } + } + } + } + } + } + + #endregion + } +} diff --git a/Fluent/Converters/ApplicationMenuRightContentExtractorConverter.cs b/Fluent.Ribbon/Converters/ApplicationMenuRightContentExtractorConverter.cs similarity index 97% rename from Fluent/Converters/ApplicationMenuRightContentExtractorConverter.cs rename to Fluent.Ribbon/Converters/ApplicationMenuRightContentExtractorConverter.cs index 8985f7414..ca0a7a817 100644 --- a/Fluent/Converters/ApplicationMenuRightContentExtractorConverter.cs +++ b/Fluent.Ribbon/Converters/ApplicationMenuRightContentExtractorConverter.cs @@ -1,47 +1,47 @@ -namespace Fluent.Converters -{ - using System; - using System.Globalization; - using System.Windows.Controls; - using System.Windows.Data; - - /// - /// Extracts right content presenter of application menu converter - /// - public class ApplicationMenuRightContentExtractorConverter : IValueConverter - { - #region Implementation of IValueConverter - - /// - /// Converts a value. - /// - /// - /// A converted value. If the method returns null, the valid null value is used. - /// - /// The value produced by the binding source.The type of the binding target property.The converter parameter to use.The culture to use in the converter. - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - var menu = value as ApplicationMenu; - if (menu != null) - { - return menu.Template.FindName("PART_RightContentPresenter", menu) as ContentPresenter; - } - - return value; - } - - /// - /// Converts a value. - /// - /// - /// A converted value. If the method returns null, the valid null value is used. - /// - /// The value that is produced by the binding target.The type to convert to.The converter parameter to use.The culture to use in the converter. - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - return value; - } - - #endregion - } +namespace Fluent.Converters +{ + using System; + using System.Globalization; + using System.Windows.Controls; + using System.Windows.Data; + + /// + /// Extracts right content presenter of application menu converter + /// + public class ApplicationMenuRightContentExtractorConverter : IValueConverter + { + #region Implementation of IValueConverter + + /// + /// Converts a value. + /// + /// + /// A converted value. If the method returns null, the valid null value is used. + /// + /// The value produced by the binding source.The type of the binding target property.The converter parameter to use.The culture to use in the converter. + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + var menu = value as ApplicationMenu; + if (menu != null) + { + return menu.Template.FindName("PART_RightContentPresenter", menu) as ContentPresenter; + } + + return value; + } + + /// + /// Converts a value. + /// + /// + /// A converted value. If the method returns null, the valid null value is used. + /// + /// The value that is produced by the binding target.The type to convert to.The converter parameter to use.The culture to use in the converter. + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + return value; + } + + #endregion + } } \ No newline at end of file diff --git a/Fluent/Converters/ColorToSolidColorBrushConverter.cs b/Fluent.Ribbon/Converters/ColorToSolidColorBrushConverter.cs similarity index 100% rename from Fluent/Converters/ColorToSolidColorBrushConverter.cs rename to Fluent.Ribbon/Converters/ColorToSolidColorBrushConverter.cs diff --git a/Fluent/Converters/IconConverter.cs b/Fluent.Ribbon/Converters/IconConverter.cs similarity index 96% rename from Fluent/Converters/IconConverter.cs rename to Fluent.Ribbon/Converters/IconConverter.cs index e2932ea55..eb670dd43 100644 --- a/Fluent/Converters/IconConverter.cs +++ b/Fluent.Ribbon/Converters/IconConverter.cs @@ -1,132 +1,132 @@ -using System; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Windows; -using System.Windows.Data; -using System.Windows.Interop; -using System.Windows.Media; -using System.Windows.Media.Imaging; - -namespace Fluent -{ - using Fluent.Metro.Native; - - /// - /// Icon converter provides default icon if user-defined is not present - /// - public sealed class IconConverter : IValueConverter - { - #region Implementation of IValueConverter - - object IValueConverter.Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - if (value == null) - { - if (Application.Current != null - && Application.Current.MainWindow != null) - { - try - { - return GetDefaultIcon((new WindowInteropHelper(Application.Current.MainWindow)).Handle) as BitmapFrame; - } - catch (InvalidOperationException) - { - return null; - } - } - - var p = Process.GetCurrentProcess(); - if (p.MainWindowHandle != IntPtr.Zero) - { - return GetDefaultIcon(p.MainWindowHandle/*(new WindowInteropHelper(Application.Current.MainWindow)).Handle*/) as BitmapFrame; - } - } - - var bitmapFrame = value as BitmapFrame; - - if (bitmapFrame == null - || bitmapFrame.Decoder == null) - { - return null; - } - - foreach (var frame in bitmapFrame.Decoder.Frames) - { - var source = GetThumbnail(frame); - - if (source != null) - { - return source; - } - } - - return value; - } - - /// - /// ThumbnailExceptionWorkArround when image cause a format exception by accessing the Thumbnail - /// - /// - /// - static BitmapSource GetThumbnail(BitmapSource frame) - { - try - { - if (frame != null - && frame.PixelWidth == 16 - && frame.PixelHeight == 16 - && (frame.Format == PixelFormats.Bgra32 || frame.Format == PixelFormats.Bgr24)) - { - return frame; - } - - return null; - } - catch (Exception) - { - return null; - } - } - - [SuppressMessage("Microsoft.Design", "CA1031")] - static ImageSource GetDefaultIcon(IntPtr hwnd) - { - if (hwnd != IntPtr.Zero) - { - try - { - var zero = NativeMethods.SendMessage(hwnd, 0x7f, new IntPtr(2), IntPtr.Zero); - - if (zero == IntPtr.Zero) - { - zero = UnsafeNativeMethods.GetClassLong(hwnd, -34); - } - - if (zero == IntPtr.Zero) - { - zero = NativeMethods.LoadImage(IntPtr.Zero, new IntPtr(0x7f00), 1, (int)SystemParameters.SmallIconWidth, (int)SystemParameters.SmallIconHeight, 0x8000); - } - - if (zero != IntPtr.Zero) - { - return BitmapFrame.Create(Imaging.CreateBitmapSourceFromHIcon(zero, Int32Rect.Empty, BitmapSizeOptions.FromWidthAndHeight((int)SystemParameters.SmallIconWidth, (int)SystemParameters.SmallIconHeight))); - } - } - catch - { - return null; - } - } - - return null; - } - - object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - return Binding.DoNothing; - } - - #endregion - } +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Windows; +using System.Windows.Data; +using System.Windows.Interop; +using System.Windows.Media; +using System.Windows.Media.Imaging; + +namespace Fluent +{ + using Fluent.Metro.Native; + + /// + /// Icon converter provides default icon if user-defined is not present + /// + public sealed class IconConverter : IValueConverter + { + #region Implementation of IValueConverter + + object IValueConverter.Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value == null) + { + if (Application.Current != null + && Application.Current.MainWindow != null) + { + try + { + return GetDefaultIcon((new WindowInteropHelper(Application.Current.MainWindow)).Handle) as BitmapFrame; + } + catch (InvalidOperationException) + { + return null; + } + } + + var p = Process.GetCurrentProcess(); + if (p.MainWindowHandle != IntPtr.Zero) + { + return GetDefaultIcon(p.MainWindowHandle/*(new WindowInteropHelper(Application.Current.MainWindow)).Handle*/) as BitmapFrame; + } + } + + var bitmapFrame = value as BitmapFrame; + + if (bitmapFrame == null + || bitmapFrame.Decoder == null) + { + return null; + } + + foreach (var frame in bitmapFrame.Decoder.Frames) + { + var source = GetThumbnail(frame); + + if (source != null) + { + return source; + } + } + + return value; + } + + /// + /// ThumbnailExceptionWorkArround when image cause a format exception by accessing the Thumbnail + /// + /// + /// + static BitmapSource GetThumbnail(BitmapSource frame) + { + try + { + if (frame != null + && frame.PixelWidth == 16 + && frame.PixelHeight == 16 + && (frame.Format == PixelFormats.Bgra32 || frame.Format == PixelFormats.Bgr24)) + { + return frame; + } + + return null; + } + catch (Exception) + { + return null; + } + } + + [SuppressMessage("Microsoft.Design", "CA1031")] + static ImageSource GetDefaultIcon(IntPtr hwnd) + { + if (hwnd != IntPtr.Zero) + { + try + { + var zero = NativeMethods.SendMessage(hwnd, 0x7f, new IntPtr(2), IntPtr.Zero); + + if (zero == IntPtr.Zero) + { + zero = UnsafeNativeMethods.GetClassLong(hwnd, -34); + } + + if (zero == IntPtr.Zero) + { + zero = NativeMethods.LoadImage(IntPtr.Zero, new IntPtr(0x7f00), 1, (int)SystemParameters.SmallIconWidth, (int)SystemParameters.SmallIconHeight, 0x8000); + } + + if (zero != IntPtr.Zero) + { + return BitmapFrame.Create(Imaging.CreateBitmapSourceFromHIcon(zero, Int32Rect.Empty, BitmapSizeOptions.FromWidthAndHeight((int)SystemParameters.SmallIconWidth, (int)SystemParameters.SmallIconHeight))); + } + } + catch + { + return null; + } + } + + return null; + } + + object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + return Binding.DoNothing; + } + + #endregion + } } \ No newline at end of file diff --git a/Fluent/Converters/InvertNumericConverter.cs b/Fluent.Ribbon/Converters/InvertNumericConverter.cs similarity index 97% rename from Fluent/Converters/InvertNumericConverter.cs rename to Fluent.Ribbon/Converters/InvertNumericConverter.cs index ccbab4978..0f5d7a089 100644 --- a/Fluent/Converters/InvertNumericConverter.cs +++ b/Fluent.Ribbon/Converters/InvertNumericConverter.cs @@ -1,76 +1,76 @@ -namespace Fluent.Converters -{ - using System; - using System.Globalization; - using System.Windows.Data; - - /// - /// Used to invert numbers - /// - public class InvertNumericConverter : IValueConverter - { - #region Implementation of IValueConverter - - /// - /// Converts a value. - /// - /// - /// A converted value. If the method returns null, the valid null value is used. - /// - /// The value produced by the binding source.The type of the binding target property.The converter parameter to use.The culture to use in the converter. - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - { - var numericValue = value as float?; - - if (numericValue != null) - { - return numericValue * -1; - } - } - - { - var numericValue = value as double?; - - if (numericValue != null) - { - return numericValue * -1; - } - } - - { - var numericValue = value as int?; - - if (numericValue != null) - { - return numericValue * -1; - } - } - - { - var numericValue = value as long?; - - if (numericValue != null) - { - return numericValue * -1; - } - } - - return value; - } - - /// - /// Converts a value. - /// - /// - /// A converted value. If the method returns null, the valid null value is used. - /// - /// The value that is produced by the binding target.The type to convert to.The converter parameter to use.The culture to use in the converter. - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - return this.Convert(value, targetType, parameter, culture); - } - - #endregion - } +namespace Fluent.Converters +{ + using System; + using System.Globalization; + using System.Windows.Data; + + /// + /// Used to invert numbers + /// + public class InvertNumericConverter : IValueConverter + { + #region Implementation of IValueConverter + + /// + /// Converts a value. + /// + /// + /// A converted value. If the method returns null, the valid null value is used. + /// + /// The value produced by the binding source.The type of the binding target property.The converter parameter to use.The culture to use in the converter. + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + { + var numericValue = value as float?; + + if (numericValue != null) + { + return numericValue * -1; + } + } + + { + var numericValue = value as double?; + + if (numericValue != null) + { + return numericValue * -1; + } + } + + { + var numericValue = value as int?; + + if (numericValue != null) + { + return numericValue * -1; + } + } + + { + var numericValue = value as long?; + + if (numericValue != null) + { + return numericValue * -1; + } + } + + return value; + } + + /// + /// Converts a value. + /// + /// + /// A converted value. If the method returns null, the valid null value is used. + /// + /// The value that is produced by the binding target.The type to convert to.The converter parameter to use.The culture to use in the converter. + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + return this.Convert(value, targetType, parameter, culture); + } + + #endregion + } } \ No newline at end of file diff --git a/Fluent/Converters/ObjectToImageConverter.cs b/Fluent.Ribbon/Converters/ObjectToImageConverter.cs similarity index 97% rename from Fluent/Converters/ObjectToImageConverter.cs rename to Fluent.Ribbon/Converters/ObjectToImageConverter.cs index 385dcdd96..bdd5fb9dd 100644 --- a/Fluent/Converters/ObjectToImageConverter.cs +++ b/Fluent.Ribbon/Converters/ObjectToImageConverter.cs @@ -1,191 +1,191 @@ -namespace Fluent.Converters -{ - using System; - using System.Diagnostics; - using System.Globalization; - using System.Linq; - using System.Net.Cache; - using System.Windows; - using System.Windows.Controls; - using System.Windows.Data; - using System.Windows.Media; - using System.Windows.Media.Imaging; - - /// - /// Converts string or ImageSource to Image control - /// - public class ObjectToImageConverter : IValueConverter - { - #region Implementation of IValueConverter - - /// - /// Converts a value. - /// - /// - /// A converted value. If the method returns null, the valid null value is used. - /// - /// The value produced by the binding source.The type of the binding target property.The converter parameter to use.The culture to use in the converter. - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - var desiredSize = double.NaN; - - if (parameter != null) - { - try - { - desiredSize = System.Convert.ToDouble(parameter); - } - catch (Exception ex) - { - Debug.WriteLine(ex); - } - } - - var imagePath = value as string; - if (imagePath != null) - { - return CreateImage(imagePath, desiredSize); - } - - var imageUri = value as Uri; - if (imageUri != null) - { - return CreateImage(imageUri, desiredSize); - } - - var imageSource = value as ImageSource; - - if (imageSource == null) - { - return value; - } - - var image = new Image - { - // We have to use a frozen instance. Otherwise we run into trouble if the same instance is used in multiple locations. - // In case of BitmapImage it even gets worse when using the same Uri... - Source = (ImageSource)ExtractImage(imageSource, desiredSize).GetAsFrozen() - }; - - return image; - } - - /// - /// Converts a value. - /// - /// - /// A converted value. If the method returns null, the valid null value is used. - /// - /// The value that is produced by the binding target.The type to convert to.The converter parameter to use.The culture to use in the converter. - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - return Binding.DoNothing; - } - - #endregion - - private static Image CreateImage(string imagePath, double desiredSize) - { - if (double.IsNaN(desiredSize) == false - && imagePath.EndsWith(".ico")) - { - return new Image - { - Source = ExtractImageFromIcoFile(imagePath, desiredSize) - }; - } - - return new Image - { - Source = new BitmapImage(new Uri(imagePath, UriKind.RelativeOrAbsolute), new RequestCachePolicy(RequestCacheLevel.NoCacheNoStore)) - }; - } - private static Image CreateImage(Uri imageUri, double desiredSize) - { - if (double.IsNaN(desiredSize) == false - && imageUri.AbsolutePath.EndsWith(".ico")) - { - return new Image - { - Source = ExtractImageFromIcoFile(imageUri, desiredSize) - }; - } - - return new Image - { - Source = new BitmapImage(imageUri, new RequestCachePolicy(RequestCacheLevel.NoCacheNoStore)) - }; - } - - private static ImageSource ExtractImageFromIcoFile(string imagePath, double desiredSize) - { - return ExtractImageFromIcoFile( - new Uri("pack://application:,,," + imagePath, UriKind.RelativeOrAbsolute), - desiredSize - ); - } - - private static ImageSource ExtractImageFromIcoFile(Uri imageUri, double desiredSize) - { - var decoder = BitmapDecoder.Create( - imageUri, - BitmapCreateOptions.DelayCreation | BitmapCreateOptions.IgnoreImageCache, - BitmapCacheOption.None - ); - - return ExtractImage(decoder, desiredSize); - } - - private static ImageSource ExtractImage(ImageSource imageSource, double desiredSize) - { - if (double.IsNaN(desiredSize)) - { - return imageSource; - } - - var bitmapFrame = imageSource as BitmapFrame; - - // We may have some other type of ImageSource - // that doesn't have a notion of frames or decoder - if (bitmapFrame == null - || bitmapFrame.Decoder == null) - { - return imageSource; - } - - return ExtractImage(bitmapFrame.Decoder, desiredSize); - } - - private static ImageSource ExtractImage(BitmapDecoder decoder, double desiredSize) - { - var dpiFactor = 1.0; - - if (Application.Current.MainWindow != null) - { - // dpi.M11 = dpiX, dpi.M22 = dpiY - var presentationSource = PresentationSource.FromVisual(Application.Current.MainWindow); - - if (presentationSource != null) - { - if (presentationSource.CompositionTarget != null) - { - var dpi = presentationSource.CompositionTarget.TransformToDevice; - dpiFactor = dpi.M11; - } - } - } - - var result = decoder.Frames - .OrderBy(f => f.Width) - .FirstOrDefault(f => f.Width >= desiredSize * dpiFactor); - - // if there is no matching frame, get the largest frame - if (ReferenceEquals(result, default(BitmapFrame))) - { - result = decoder.Frames.OrderBy(f => f.Width).Last(); - } - - return result; - } - } -} +namespace Fluent.Converters +{ + using System; + using System.Diagnostics; + using System.Globalization; + using System.Linq; + using System.Net.Cache; + using System.Windows; + using System.Windows.Controls; + using System.Windows.Data; + using System.Windows.Media; + using System.Windows.Media.Imaging; + + /// + /// Converts string or ImageSource to Image control + /// + public class ObjectToImageConverter : IValueConverter + { + #region Implementation of IValueConverter + + /// + /// Converts a value. + /// + /// + /// A converted value. If the method returns null, the valid null value is used. + /// + /// The value produced by the binding source.The type of the binding target property.The converter parameter to use.The culture to use in the converter. + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + var desiredSize = double.NaN; + + if (parameter != null) + { + try + { + desiredSize = System.Convert.ToDouble(parameter); + } + catch (Exception ex) + { + Debug.WriteLine(ex); + } + } + + var imagePath = value as string; + if (imagePath != null) + { + return CreateImage(imagePath, desiredSize); + } + + var imageUri = value as Uri; + if (imageUri != null) + { + return CreateImage(imageUri, desiredSize); + } + + var imageSource = value as ImageSource; + + if (imageSource == null) + { + return value; + } + + var image = new Image + { + // We have to use a frozen instance. Otherwise we run into trouble if the same instance is used in multiple locations. + // In case of BitmapImage it even gets worse when using the same Uri... + Source = (ImageSource)ExtractImage(imageSource, desiredSize).GetAsFrozen() + }; + + return image; + } + + /// + /// Converts a value. + /// + /// + /// A converted value. If the method returns null, the valid null value is used. + /// + /// The value that is produced by the binding target.The type to convert to.The converter parameter to use.The culture to use in the converter. + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + return Binding.DoNothing; + } + + #endregion + + private static Image CreateImage(string imagePath, double desiredSize) + { + if (double.IsNaN(desiredSize) == false + && imagePath.EndsWith(".ico")) + { + return new Image + { + Source = ExtractImageFromIcoFile(imagePath, desiredSize) + }; + } + + return new Image + { + Source = new BitmapImage(new Uri(imagePath, UriKind.RelativeOrAbsolute), new RequestCachePolicy(RequestCacheLevel.NoCacheNoStore)) + }; + } + private static Image CreateImage(Uri imageUri, double desiredSize) + { + if (double.IsNaN(desiredSize) == false + && imageUri.AbsolutePath.EndsWith(".ico")) + { + return new Image + { + Source = ExtractImageFromIcoFile(imageUri, desiredSize) + }; + } + + return new Image + { + Source = new BitmapImage(imageUri, new RequestCachePolicy(RequestCacheLevel.NoCacheNoStore)) + }; + } + + private static ImageSource ExtractImageFromIcoFile(string imagePath, double desiredSize) + { + return ExtractImageFromIcoFile( + new Uri("pack://application:,,," + imagePath, UriKind.RelativeOrAbsolute), + desiredSize + ); + } + + private static ImageSource ExtractImageFromIcoFile(Uri imageUri, double desiredSize) + { + var decoder = BitmapDecoder.Create( + imageUri, + BitmapCreateOptions.DelayCreation | BitmapCreateOptions.IgnoreImageCache, + BitmapCacheOption.None + ); + + return ExtractImage(decoder, desiredSize); + } + + private static ImageSource ExtractImage(ImageSource imageSource, double desiredSize) + { + if (double.IsNaN(desiredSize)) + { + return imageSource; + } + + var bitmapFrame = imageSource as BitmapFrame; + + // We may have some other type of ImageSource + // that doesn't have a notion of frames or decoder + if (bitmapFrame == null + || bitmapFrame.Decoder == null) + { + return imageSource; + } + + return ExtractImage(bitmapFrame.Decoder, desiredSize); + } + + private static ImageSource ExtractImage(BitmapDecoder decoder, double desiredSize) + { + var dpiFactor = 1.0; + + if (Application.Current.MainWindow != null) + { + // dpi.M11 = dpiX, dpi.M22 = dpiY + var presentationSource = PresentationSource.FromVisual(Application.Current.MainWindow); + + if (presentationSource != null) + { + if (presentationSource.CompositionTarget != null) + { + var dpi = presentationSource.CompositionTarget.TransformToDevice; + dpiFactor = dpi.M11; + } + } + } + + var result = decoder.Frames + .OrderBy(f => f.Width) + .FirstOrDefault(f => f.Width >= desiredSize * dpiFactor); + + // if there is no matching frame, get the largest frame + if (ReferenceEquals(result, default(BitmapFrame))) + { + result = decoder.Frames.OrderBy(f => f.Width).Last(); + } + + return result; + } + } +} diff --git a/Fluent/Converters/SizeDefinitionConverter.cs b/Fluent.Ribbon/Converters/SizeDefinitionConverter.cs similarity index 98% rename from Fluent/Converters/SizeDefinitionConverter.cs rename to Fluent.Ribbon/Converters/SizeDefinitionConverter.cs index 9713ee0ee..739c820ca 100644 --- a/Fluent/Converters/SizeDefinitionConverter.cs +++ b/Fluent.Ribbon/Converters/SizeDefinitionConverter.cs @@ -1,36 +1,36 @@ -namespace Fluent.Converters -{ - using System; - using System.ComponentModel; - using System.Globalization; - - /// - /// Class which enables conversion from to - /// - public class SizeDefinitionConverter : TypeConverter - { - /// - /// Returns whether this converter can convert an object of the given type to the type of this converter, using the specified context. - /// - /// - /// true if this converter can perform the conversion; otherwise, false. - /// - /// An that provides a format context. A that represents the type you want to convert from. - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) - { - return sourceType.IsAssignableFrom(typeof(string)); - } - - /// - /// Converts the given object to the type of this converter, using the specified context and culture information. - /// - /// - /// An that represents the converted value. - /// - /// An that provides a format context. The to use as the current culture. The to convert. The conversion cannot be performed. - public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) - { - return new RibbonControlSizeDefinition(value as string); - } - } +namespace Fluent.Converters +{ + using System; + using System.ComponentModel; + using System.Globalization; + + /// + /// Class which enables conversion from to + /// + public class SizeDefinitionConverter : TypeConverter + { + /// + /// Returns whether this converter can convert an object of the given type to the type of this converter, using the specified context. + /// + /// + /// true if this converter can perform the conversion; otherwise, false. + /// + /// An that provides a format context. A that represents the type you want to convert from. + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + return sourceType.IsAssignableFrom(typeof(string)); + } + + /// + /// Converts the given object to the type of this converter, using the specified context and culture information. + /// + /// + /// An that represents the converted value. + /// + /// An that provides a format context. The to use as the current culture. The to convert. The conversion cannot be performed. + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + return new RibbonControlSizeDefinition(value as string); + } + } } \ No newline at end of file diff --git a/Fluent/Converters/StaticConverters.cs b/Fluent.Ribbon/Converters/StaticConverters.cs similarity index 97% rename from Fluent/Converters/StaticConverters.cs rename to Fluent.Ribbon/Converters/StaticConverters.cs index 2ffec2e79..7d83e1979 100644 --- a/Fluent/Converters/StaticConverters.cs +++ b/Fluent.Ribbon/Converters/StaticConverters.cs @@ -1,28 +1,28 @@ -namespace Fluent.Converters -{ - /// - /// Hold static instances of several commonly used converters. - /// - public static class StaticConverters - { - /// - /// Get a static instance of - /// - public static readonly InvertNumericConverter InvertNumericConverter = new InvertNumericConverter(); - - /// - /// Get a static instance of - /// - public static readonly ThicknessConverter ThicknessConverter = new ThicknessConverter(); - - /// - /// Get a static instance of - /// - public static readonly ObjectToImageConverter ObjectToImageConverter = new ObjectToImageConverter(); - - /// - /// Get a static instance of - /// - public static readonly ColorToSolidColorBrushValueConverter ColorToSolidColorBrushValueConverter = new ColorToSolidColorBrushValueConverter(); - } +namespace Fluent.Converters +{ + /// + /// Hold static instances of several commonly used converters. + /// + public static class StaticConverters + { + /// + /// Get a static instance of + /// + public static readonly InvertNumericConverter InvertNumericConverter = new InvertNumericConverter(); + + /// + /// Get a static instance of + /// + public static readonly ThicknessConverter ThicknessConverter = new ThicknessConverter(); + + /// + /// Get a static instance of + /// + public static readonly ObjectToImageConverter ObjectToImageConverter = new ObjectToImageConverter(); + + /// + /// Get a static instance of + /// + public static readonly ColorToSolidColorBrushValueConverter ColorToSolidColorBrushValueConverter = new ColorToSolidColorBrushValueConverter(); + } } \ No newline at end of file diff --git a/Fluent/Converters/ThicknessConverter.cs b/Fluent.Ribbon/Converters/ThicknessConverter.cs similarity index 98% rename from Fluent/Converters/ThicknessConverter.cs rename to Fluent.Ribbon/Converters/ThicknessConverter.cs index c79a7f2d3..93578af1d 100644 --- a/Fluent/Converters/ThicknessConverter.cs +++ b/Fluent.Ribbon/Converters/ThicknessConverter.cs @@ -1,54 +1,54 @@ -namespace Fluent.Converters -{ - using System; - using System.Globalization; - using System.Linq; - using System.Windows; - using System.Windows.Data; - - /// - /// Used to convert from four double values to - /// - public class ThicknessConverter : IMultiValueConverter - { - private static readonly System.Windows.ThicknessConverter systemThicknessConverter = new System.Windows.ThicknessConverter(); - - #region Implementation of IMultiValueConverter - - /// - /// Converts source values to a value for the binding target. The data binding engine calls this method when it propagates the values from source bindings to the binding target. - /// - /// - /// A converted value.If the method returns null, the valid null value is used.A return value of . indicates that the converter did not produce a value, and that the binding will use the if it is available, or else will use the default value.A return value of . indicates that the binding does not transfer the value or use the or the default value. - /// - /// The array of values that the source bindings in the produces. The value indicates that the source binding has no value to provide for conversion.The type of the binding target property.The converter parameter to use.The culture to use in the converter. - public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) - { - if (values.Any(x => x == DependencyProperty.UnsetValue)) - { - if (parameter != null) - { - return (Thickness)systemThicknessConverter.ConvertFromString((string)parameter); - } - - return new Thickness(); - } - - return new Thickness(System.Convert.ToDouble(values[0]), System.Convert.ToDouble(values[1]), System.Convert.ToDouble(values[2]), System.Convert.ToDouble(values[3])); - } - - /// - /// Converts a binding target value to the source binding values. - /// - /// - /// An array of values that have been converted from the target value back to the source values. - /// - /// The value that the binding target produces.The array of types to convert to. The array length indicates the number and types of values that are suggested for the method to return.The converter parameter to use.The culture to use in the converter. - public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) - { - throw new NotImplementedException(); - } - - #endregion - } +namespace Fluent.Converters +{ + using System; + using System.Globalization; + using System.Linq; + using System.Windows; + using System.Windows.Data; + + /// + /// Used to convert from four double values to + /// + public class ThicknessConverter : IMultiValueConverter + { + private static readonly System.Windows.ThicknessConverter systemThicknessConverter = new System.Windows.ThicknessConverter(); + + #region Implementation of IMultiValueConverter + + /// + /// Converts source values to a value for the binding target. The data binding engine calls this method when it propagates the values from source bindings to the binding target. + /// + /// + /// A converted value.If the method returns null, the valid null value is used.A return value of . indicates that the converter did not produce a value, and that the binding will use the if it is available, or else will use the default value.A return value of . indicates that the binding does not transfer the value or use the or the default value. + /// + /// The array of values that the source bindings in the produces. The value indicates that the source binding has no value to provide for conversion.The type of the binding target property.The converter parameter to use.The culture to use in the converter. + public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) + { + if (values.Any(x => x == DependencyProperty.UnsetValue)) + { + if (parameter != null) + { + return (Thickness)systemThicknessConverter.ConvertFromString((string)parameter); + } + + return new Thickness(); + } + + return new Thickness(System.Convert.ToDouble(values[0]), System.Convert.ToDouble(values[1]), System.Convert.ToDouble(values[2]), System.Convert.ToDouble(values[3])); + } + + /// + /// Converts a binding target value to the source binding values. + /// + /// + /// An array of values that have been converted from the target value back to the source values. + /// + /// The value that the binding target produces.The array of types to convert to. The array length indicates the number and types of values that are suggested for the method to return.The converter parameter to use.The culture to use in the converter. + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + + #endregion + } } \ No newline at end of file diff --git a/Fluent/Documents/Features.xlsx b/Fluent.Ribbon/Documents/Features.xlsx similarity index 100% rename from Fluent/Documents/Features.xlsx rename to Fluent.Ribbon/Documents/Features.xlsx diff --git a/Fluent/Documents/Fluent Ribbon Control Suite Walkthrough.docx b/Fluent.Ribbon/Documents/Fluent Ribbon Control Suite Walkthrough.docx similarity index 100% rename from Fluent/Documents/Fluent Ribbon Control Suite Walkthrough.docx rename to Fluent.Ribbon/Documents/Fluent Ribbon Control Suite Walkthrough.docx diff --git a/Fluent/Effects/GrayscaleEffect.cs b/Fluent.Ribbon/Effects/GrayscaleEffect.cs similarity index 96% rename from Fluent/Effects/GrayscaleEffect.cs rename to Fluent.Ribbon/Effects/GrayscaleEffect.cs index b8f25b6b9..9d74f4af8 100644 --- a/Fluent/Effects/GrayscaleEffect.cs +++ b/Fluent.Ribbon/Effects/GrayscaleEffect.cs @@ -1,78 +1,78 @@ -using System; -using System.ComponentModel; -using System.Windows; -using System.Windows.Media; -using System.Windows.Media.Effects; - - -namespace Fluent -{ - /// - /// An effect that turns the input into shades of a single color. - /// - public class GrayscaleEffect : ShaderEffect - { - /// - /// Dependency property for Input - /// - public static readonly DependencyProperty InputProperty = - RegisterPixelShaderSamplerProperty("Input", typeof(GrayscaleEffect), 0); - - /// - /// Dependency property for FilterColor - /// - public static readonly DependencyProperty FilterColorProperty = - DependencyProperty.Register("FilterColor", typeof(Color), typeof(GrayscaleEffect), - new UIPropertyMetadata(Color.FromArgb(255, 255, 255, 255), PixelShaderConstantCallback(0))); - - /// - /// Default constructor - /// - public GrayscaleEffect() - { - var pixelShader = new PixelShader(); - var prop = DesignerProperties.IsInDesignModeProperty; - - var isInDesignMode = (bool)DependencyPropertyDescriptor.FromProperty(prop, typeof(FrameworkElement)).Metadata.DefaultValue; - if (!isInDesignMode) - { - pixelShader.UriSource = new Uri("/Fluent;component/Themes/Office2010/Effects/Grayscale.ps", UriKind.Relative); - } - - this.PixelShader = pixelShader; - - this.UpdateShaderValue(InputProperty); - this.UpdateShaderValue(FilterColorProperty); - } - - /// - /// Impicit input - /// - public Brush Input - { - get - { - return ((Brush)(this.GetValue(InputProperty))); - } - set - { - this.SetValue(InputProperty, value); - } - } - - /// - /// The color used to tint the input. - /// - public Color FilterColor - { - get - { - return ((Color)(this.GetValue(FilterColorProperty))); - } - set - { - this.SetValue(FilterColorProperty, value); - } - } - } +using System; +using System.ComponentModel; +using System.Windows; +using System.Windows.Media; +using System.Windows.Media.Effects; + + +namespace Fluent +{ + /// + /// An effect that turns the input into shades of a single color. + /// + public class GrayscaleEffect : ShaderEffect + { + /// + /// Dependency property for Input + /// + public static readonly DependencyProperty InputProperty = + RegisterPixelShaderSamplerProperty("Input", typeof(GrayscaleEffect), 0); + + /// + /// Dependency property for FilterColor + /// + public static readonly DependencyProperty FilterColorProperty = + DependencyProperty.Register("FilterColor", typeof(Color), typeof(GrayscaleEffect), + new UIPropertyMetadata(Color.FromArgb(255, 255, 255, 255), PixelShaderConstantCallback(0))); + + /// + /// Default constructor + /// + public GrayscaleEffect() + { + var pixelShader = new PixelShader(); + var prop = DesignerProperties.IsInDesignModeProperty; + + var isInDesignMode = (bool)DependencyPropertyDescriptor.FromProperty(prop, typeof(FrameworkElement)).Metadata.DefaultValue; + if (!isInDesignMode) + { + pixelShader.UriSource = new Uri("/Fluent;component/Themes/Office2010/Effects/Grayscale.ps", UriKind.Relative); + } + + this.PixelShader = pixelShader; + + this.UpdateShaderValue(InputProperty); + this.UpdateShaderValue(FilterColorProperty); + } + + /// + /// Impicit input + /// + public Brush Input + { + get + { + return ((Brush)(this.GetValue(InputProperty))); + } + set + { + this.SetValue(InputProperty, value); + } + } + + /// + /// The color used to tint the input. + /// + public Color FilterColor + { + get + { + return ((Color)(this.GetValue(FilterColorProperty))); + } + set + { + this.SetValue(FilterColorProperty, value); + } + } + } } \ No newline at end of file diff --git a/Fluent/Enumerations/RibbonControlSize.cs b/Fluent.Ribbon/Enumerations/RibbonControlSize.cs similarity index 95% rename from Fluent/Enumerations/RibbonControlSize.cs rename to Fluent.Ribbon/Enumerations/RibbonControlSize.cs index fe57ae5ea..b1c069935 100644 --- a/Fluent/Enumerations/RibbonControlSize.cs +++ b/Fluent.Ribbon/Enumerations/RibbonControlSize.cs @@ -1,23 +1,23 @@ -namespace Fluent -{ - /// - /// Represents logical sizes of a ribbon control - /// - public enum RibbonControlSize - { - /// - /// Large size of a control - /// - Large = 0, - - /// - /// Middle size of a control - /// - Middle, - - /// - /// Small size of a control - /// - Small - } +namespace Fluent +{ + /// + /// Represents logical sizes of a ribbon control + /// + public enum RibbonControlSize + { + /// + /// Large size of a control + /// + Large = 0, + + /// + /// Middle size of a control + /// + Middle, + + /// + /// Small size of a control + /// + Small + } } \ No newline at end of file diff --git a/Fluent/Enumerations/RibbonGroupBoxState.cs b/Fluent.Ribbon/Enumerations/RibbonGroupBoxState.cs similarity index 96% rename from Fluent/Enumerations/RibbonGroupBoxState.cs rename to Fluent.Ribbon/Enumerations/RibbonGroupBoxState.cs index fb404150a..f209b741f 100644 --- a/Fluent/Enumerations/RibbonGroupBoxState.cs +++ b/Fluent.Ribbon/Enumerations/RibbonGroupBoxState.cs @@ -1,33 +1,33 @@ -namespace Fluent -{ - /// - /// Represents states of ribbon group - /// - public enum RibbonGroupBoxState - { - /// - /// Large. All controls in the group will try to be large size - /// - Large = 0, - - /// - /// Middle. All controls in the group will try to be middle size - /// - Middle, - - /// - /// Small. All controls in the group will try to be small size - /// - Small, - - /// - /// Collapsed. Group will collapse its content in a single button - /// - Collapsed, - - /// - /// QuickAccess. Group will collapse its content in a single button in quick access toolbar - /// - QuickAccess - } +namespace Fluent +{ + /// + /// Represents states of ribbon group + /// + public enum RibbonGroupBoxState + { + /// + /// Large. All controls in the group will try to be large size + /// + Large = 0, + + /// + /// Middle. All controls in the group will try to be middle size + /// + Middle, + + /// + /// Small. All controls in the group will try to be small size + /// + Small, + + /// + /// Collapsed. Group will collapse its content in a single button + /// + Collapsed, + + /// + /// QuickAccess. Group will collapse its content in a single button in quick access toolbar + /// + QuickAccess + } } \ No newline at end of file diff --git a/Fluent/Extensibility/IRibbonSizeChangedSink.cs b/Fluent.Ribbon/Extensibility/IRibbonSizeChangedSink.cs similarity index 97% rename from Fluent/Extensibility/IRibbonSizeChangedSink.cs rename to Fluent.Ribbon/Extensibility/IRibbonSizeChangedSink.cs index 5b9d72a7b..8eeea7c2f 100644 --- a/Fluent/Extensibility/IRibbonSizeChangedSink.cs +++ b/Fluent.Ribbon/Extensibility/IRibbonSizeChangedSink.cs @@ -1,15 +1,15 @@ -namespace Fluent.Extensibility -{ - /// - /// Interface which is used to signal size changes - /// - public interface IRibbonSizeChangedSink - { - /// - /// Called when the size is changed - /// - /// Size before change - /// Size after change - void OnSizePropertyChanged(RibbonControlSize previous, RibbonControlSize current); - } +namespace Fluent.Extensibility +{ + /// + /// Interface which is used to signal size changes + /// + public interface IRibbonSizeChangedSink + { + /// + /// Called when the size is changed + /// + /// Size before change + /// Size after change + void OnSizePropertyChanged(RibbonControlSize previous, RibbonControlSize current); + } } \ No newline at end of file diff --git a/Fluent/Extensions/DispatcherExtensions.cs b/Fluent.Ribbon/Extensions/DispatcherExtensions.cs similarity index 96% rename from Fluent/Extensions/DispatcherExtensions.cs rename to Fluent.Ribbon/Extensions/DispatcherExtensions.cs index ccb9131ce..6320ff48f 100644 --- a/Fluent/Extensions/DispatcherExtensions.cs +++ b/Fluent.Ribbon/Extensions/DispatcherExtensions.cs @@ -1,54 +1,54 @@ -namespace Fluent.Extensions -{ - using System; - using System.Windows.Threading; - - internal static class DispatcherExtensions - { - public static void RunInDispatcherAsync(this DispatcherObject dispatcher, Action action, DispatcherPriority priority = DispatcherPriority.Normal) - { - if (dispatcher == null) - { - action(); - return; - } - - dispatcher.Dispatcher.RunInDispatcherAsync(action, priority); - } - - public static void RunInDispatcherAsync(this Dispatcher dispatcher, Action action, DispatcherPriority priority = DispatcherPriority.Normal) - { - if (dispatcher == null) - { - action(); - } - else - { - dispatcher.BeginInvoke(priority, action); - } - } - - public static void RunInDispatcher(this DispatcherObject dispatcher, Action action, DispatcherPriority priority = DispatcherPriority.Normal) - { - if (dispatcher == null) - { - action(); - return; - } - - dispatcher.Dispatcher.RunInDispatcher(action, priority); - } - - public static void RunInDispatcher(this Dispatcher dispatcher, Action action, DispatcherPriority priority = DispatcherPriority.Normal) - { - if (dispatcher == null) - { - action(); - } - else - { - dispatcher.Invoke(priority, action); - } - } - } +namespace Fluent.Extensions +{ + using System; + using System.Windows.Threading; + + internal static class DispatcherExtensions + { + public static void RunInDispatcherAsync(this DispatcherObject dispatcher, Action action, DispatcherPriority priority = DispatcherPriority.Normal) + { + if (dispatcher == null) + { + action(); + return; + } + + dispatcher.Dispatcher.RunInDispatcherAsync(action, priority); + } + + public static void RunInDispatcherAsync(this Dispatcher dispatcher, Action action, DispatcherPriority priority = DispatcherPriority.Normal) + { + if (dispatcher == null) + { + action(); + } + else + { + dispatcher.BeginInvoke(priority, action); + } + } + + public static void RunInDispatcher(this DispatcherObject dispatcher, Action action, DispatcherPriority priority = DispatcherPriority.Normal) + { + if (dispatcher == null) + { + action(); + return; + } + + dispatcher.Dispatcher.RunInDispatcher(action, priority); + } + + public static void RunInDispatcher(this Dispatcher dispatcher, Action action, DispatcherPriority priority = DispatcherPriority.Normal) + { + if (dispatcher == null) + { + action(); + } + else + { + dispatcher.Invoke(priority, action); + } + } + } } \ No newline at end of file diff --git a/Fluent/Fluent dotNET 4.0.csproj.DotSettings b/Fluent.Ribbon/Fluent dotNET 4.0.csproj.DotSettings similarity index 100% rename from Fluent/Fluent dotNET 4.0.csproj.DotSettings rename to Fluent.Ribbon/Fluent dotNET 4.0.csproj.DotSettings diff --git a/Fluent/Fluent dotNET 4.5.csproj.DotSettings b/Fluent.Ribbon/Fluent dotNET 4.5.csproj.DotSettings similarity index 100% rename from Fluent/Fluent dotNET 4.5.csproj.DotSettings rename to Fluent.Ribbon/Fluent dotNET 4.5.csproj.DotSettings diff --git a/Fluent/Fluent dotNET 4.0.csproj b/Fluent.Ribbon/Fluent.Ribbon.NET 4.0.csproj similarity index 97% rename from Fluent/Fluent dotNET 4.0.csproj rename to Fluent.Ribbon/Fluent.Ribbon.NET 4.0.csproj index 62791afd6..4ed1382d8 100644 --- a/Fluent/Fluent dotNET 4.0.csproj +++ b/Fluent.Ribbon/Fluent.Ribbon.NET 4.0.csproj @@ -1,780 +1,780 @@ - - - - Debug - AnyCPU - 9.0.30729 - 2.0 - {281095D8-D8B3-4A7F-8896-646483FB685C} - Library - Properties - Fluent - Fluent - v4.0 - 512 - {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - true - Properties\FluentStrongName.snk - true - 4.0.20621.0 - ..\build\obj\NET 4.0 - $(BaseIntermediateOutputPath)\$(Configuration)\ - - - true - full - false - ..\build\bin\NET 4.0\Debug\ - TRACE;DEBUG;NET40;CODE_ANALYSIS - prompt - 4 - - - false - - - MinimumRecommendedRules.ruleset - false - - - pdbonly - true - ..\build\bin\NET 4.0\Release\ - TRACE;NET40;CODE_ANALYSIS - prompt - 4 - bin\NET 4.0\Release\Fluent.XML - true - - - MinimumRecommendedRules.ruleset - true - false - - - - ..\packages\ControlzEx.2.0.0-dev7\lib\net40\ControlzEx.dll - True - - - - - - - ..\packages\ControlzEx.2.0.0-dev7\lib\net40\System.Windows.Interactivity.dll - True - - - - - - - - Properties\GlobalAssemblyInfo.cs - - - - - - - - - - Code - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Code - - - - - - - - - - - - - - - - - - - - - - - - - - Designer - MSBuild:Compile - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - Designer - MSBuild:Compile - - - MSBuild:Compile - Designer - - - Designer - MSBuild:Compile - - - MSBuild:Compile - Designer - - - Designer - MSBuild:Compile - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - Designer - MSBuild:Compile - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - Designer - MSBuild:Compile - - - MSBuild:Compile - Designer - - - Designer - MSBuild:Compile - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - Designer - MSBuild:Compile - - - MSBuild:Compile - Designer - - - Designer - MSBuild:Compile - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - Designer - MSBuild:Compile - - - Designer - MSBuild:Compile - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - Designer - MSBuild:Compile - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - Designer - MSBuild:Compile - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - SET tmpError=0 -"$(ProjectDir)Themes\XamlCombine.exe" Office2010\Generic.txt Office2010\Generic.xaml -SET tmpError=%25tmpError%25 + %25ERRORLEVEL%25 -"$(ProjectDir)Themes\XamlCombine.exe" Office2013\Generic.txt Office2013\Generic.xaml -SET tmpError=%25tmpError%25 + %25ERRORLEVEL%25 -"$(ProjectDir)Themes\XamlCombine.exe" Windows8\Generic.txt Windows8\Generic.xaml -SET /a tmpError=%25tmpError%25 + %25ERRORLEVEL%25 -SET ERRORLEVEL=%25tmpError%25 - + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {281095D8-D8B3-4A7F-8896-646483FB685C} + Library + Properties + Fluent + Fluent + v4.0 + 512 + {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + true + Properties\FluentStrongName.snk + true + 4.0.20621.0 + ..\build\obj\NET 4.0 + $(BaseIntermediateOutputPath)\$(Configuration)\ + + + true + full + false + ..\build\bin\NET 4.0\Debug\ + TRACE;DEBUG;NET40;CODE_ANALYSIS + prompt + 4 + + + false + + + MinimumRecommendedRules.ruleset + false + + + pdbonly + true + ..\build\bin\NET 4.0\Release\ + TRACE;NET40;CODE_ANALYSIS + prompt + 4 + bin\NET 4.0\Release\Fluent.XML + true + + + MinimumRecommendedRules.ruleset + true + false + + + + ..\packages\ControlzEx.2.0.0-dev7\lib\net40\ControlzEx.dll + True + + + + + + + ..\packages\ControlzEx.2.0.0-dev7\lib\net40\System.Windows.Interactivity.dll + True + + + + + + + + Properties\GlobalAssemblyInfo.cs + + + + + + + + + + Code + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Code + + + + + + + + + + + + + + + + + + + + + + + + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SET tmpError=0 +"$(ProjectDir)Themes\XamlCombine.exe" Office2010\Generic.txt Office2010\Generic.xaml +SET tmpError=%25tmpError%25 + %25ERRORLEVEL%25 +"$(ProjectDir)Themes\XamlCombine.exe" Office2013\Generic.txt Office2013\Generic.xaml +SET tmpError=%25tmpError%25 + %25ERRORLEVEL%25 +"$(ProjectDir)Themes\XamlCombine.exe" Windows8\Generic.txt Windows8\Generic.xaml +SET /a tmpError=%25tmpError%25 + %25ERRORLEVEL%25 +SET ERRORLEVEL=%25tmpError%25 + \ No newline at end of file diff --git a/Fluent.Ribbon/Fluent.Ribbon.NET 4.0.csproj.DotSettings b/Fluent.Ribbon/Fluent.Ribbon.NET 4.0.csproj.DotSettings new file mode 100644 index 000000000..662f95686 --- /dev/null +++ b/Fluent.Ribbon/Fluent.Ribbon.NET 4.0.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp50 \ No newline at end of file diff --git a/Fluent/Fluent dotNET 4.5.csproj b/Fluent.Ribbon/Fluent.Ribbon.NET 4.5.csproj similarity index 97% rename from Fluent/Fluent dotNET 4.5.csproj rename to Fluent.Ribbon/Fluent.Ribbon.NET 4.5.csproj index 7bbe20997..ef65fdb34 100644 --- a/Fluent/Fluent dotNET 4.5.csproj +++ b/Fluent.Ribbon/Fluent.Ribbon.NET 4.5.csproj @@ -1,780 +1,780 @@ - - - - Debug - AnyCPU - 9.0.30729 - 2.0 - {4C92FCF4-3561-499F-BC5B-F2F089863047} - Library - Properties - Fluent - Fluent - v4.5 - 512 - {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - true - Properties\FluentStrongName.snk - true - 4.0.20621.0 - ..\build\obj\NET 4.5 - $(BaseIntermediateOutputPath)\$(Configuration)\ - - - true - full - false - ..\build\bin\NET 4.5\Debug\ - TRACE;DEBUG;CODE_ANALYSIS;NET45 - prompt - 4 - - - false - - - MinimumRecommendedRules.ruleset - false - - - pdbonly - true - ..\build\bin\NET 4.5\Release\ - TRACE;CODE_ANALYSIS;NET45 - prompt - 4 - bin\NET 4.5\Release\Fluent.XML - true - - - MinimumRecommendedRules.ruleset - true - false - - - - ..\packages\ControlzEx.2.0.0-dev7\lib\net45\ControlzEx.dll - True - - - - - - - ..\packages\ControlzEx.2.0.0-dev7\lib\net45\System.Windows.Interactivity.dll - True - - - - - - - - Properties\GlobalAssemblyInfo.cs - - - - - - - - - - Code - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Code - - - - - - - - - - - - - - - - - - - - - - - - - - Designer - MSBuild:Compile - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - Designer - MSBuild:Compile - - - MSBuild:Compile - Designer - - - Designer - MSBuild:Compile - - - MSBuild:Compile - Designer - - - Designer - MSBuild:Compile - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - Designer - MSBuild:Compile - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - Designer - MSBuild:Compile - - - MSBuild:Compile - Designer - - - Designer - MSBuild:Compile - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - Designer - MSBuild:Compile - - - MSBuild:Compile - Designer - - - Designer - MSBuild:Compile - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - Designer - MSBuild:Compile - - - Designer - MSBuild:Compile - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - Designer - MSBuild:Compile - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - Designer - MSBuild:Compile - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - SET tmpError=0 -"$(ProjectDir)Themes\XamlCombine.exe" Office2010\Generic.txt Office2010\Generic.xaml -SET tmpError=%25tmpError%25 + %25ERRORLEVEL%25 -"$(ProjectDir)Themes\XamlCombine.exe" Office2013\Generic.txt Office2013\Generic.xaml -SET tmpError=%25tmpError%25 + %25ERRORLEVEL%25 -"$(ProjectDir)Themes\XamlCombine.exe" Windows8\Generic.txt Windows8\Generic.xaml -SET /a tmpError=%25tmpError%25 + %25ERRORLEVEL%25 -SET ERRORLEVEL=%25tmpError%25 - + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {4C92FCF4-3561-499F-BC5B-F2F089863047} + Library + Properties + Fluent + Fluent + v4.5 + 512 + {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + true + Properties\FluentStrongName.snk + true + 4.0.20621.0 + ..\build\obj\NET 4.5 + $(BaseIntermediateOutputPath)\$(Configuration)\ + + + true + full + false + ..\build\bin\NET 4.5\Debug\ + TRACE;DEBUG;CODE_ANALYSIS;NET45 + prompt + 4 + + + false + + + MinimumRecommendedRules.ruleset + false + + + pdbonly + true + ..\build\bin\NET 4.5\Release\ + TRACE;CODE_ANALYSIS;NET45 + prompt + 4 + bin\NET 4.5\Release\Fluent.XML + true + + + MinimumRecommendedRules.ruleset + true + false + + + + ..\packages\ControlzEx.2.0.0-dev7\lib\net45\ControlzEx.dll + True + + + + + + + ..\packages\ControlzEx.2.0.0-dev7\lib\net45\System.Windows.Interactivity.dll + True + + + + + + + + Properties\GlobalAssemblyInfo.cs + + + + + + + + + + Code + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Code + + + + + + + + + + + + + + + + + + + + + + + + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SET tmpError=0 +"$(ProjectDir)Themes\XamlCombine.exe" Office2010\Generic.txt Office2010\Generic.xaml +SET tmpError=%25tmpError%25 + %25ERRORLEVEL%25 +"$(ProjectDir)Themes\XamlCombine.exe" Office2013\Generic.txt Office2013\Generic.xaml +SET tmpError=%25tmpError%25 + %25ERRORLEVEL%25 +"$(ProjectDir)Themes\XamlCombine.exe" Windows8\Generic.txt Windows8\Generic.xaml +SET /a tmpError=%25tmpError%25 + %25ERRORLEVEL%25 +SET ERRORLEVEL=%25tmpError%25 + \ No newline at end of file diff --git a/Fluent.Ribbon/Fluent.Ribbon.NET 4.5.csproj.DotSettings b/Fluent.Ribbon/Fluent.Ribbon.NET 4.5.csproj.DotSettings new file mode 100644 index 000000000..662f95686 --- /dev/null +++ b/Fluent.Ribbon/Fluent.Ribbon.NET 4.5.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp50 \ No newline at end of file diff --git a/Fluent/Helpers/FrameworkHelper.cs b/Fluent.Ribbon/Helpers/FrameworkHelper.cs similarity index 97% rename from Fluent/Helpers/FrameworkHelper.cs rename to Fluent.Ribbon/Helpers/FrameworkHelper.cs index 693d28e93..f72b62c7d 100644 --- a/Fluent/Helpers/FrameworkHelper.cs +++ b/Fluent.Ribbon/Helpers/FrameworkHelper.cs @@ -1,52 +1,52 @@ -namespace Fluent -{ - using System; - using System.Reflection; - using System.Windows; - using System.Windows.Media; - - /// - /// Represents class to determine .NET Framework version difference - /// - public static class FrameworkHelper - { - /// - /// Version of WPF - /// - public static readonly Version PresentationFrameworkVersion = Assembly.GetAssembly(typeof(Window)).GetName().Version; - - /// - /// Gets UseLayoutRounding attached property value - /// - /// - /// - public static bool GetUseLayoutRounding(DependencyObject obj) - { - return (bool)obj.GetValue(UseLayoutRoundingProperty); - } - - /// - /// Gets UseLayoutRounding attached property value - /// - /// - /// - public static void SetUseLayoutRounding(DependencyObject obj, bool value) - { - obj.SetValue(UseLayoutRoundingProperty, value); - } - - /// - /// Using a DependencyProperty as the backing store for UseLayoutRounding. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty UseLayoutRoundingProperty = - DependencyProperty.RegisterAttached("UseLayoutRounding", typeof(bool), typeof(FrameworkHelper), new UIPropertyMetadata(false, OnUseLayoutRoundingChanged)); - - private static void OnUseLayoutRoundingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - d.SetValue(UIElement.SnapsToDevicePixelsProperty, true); - RenderOptions.SetClearTypeHint(d, ClearTypeHint.Enabled); - d.SetValue(FrameworkElement.UseLayoutRoundingProperty, true); - } - - } +namespace Fluent +{ + using System; + using System.Reflection; + using System.Windows; + using System.Windows.Media; + + /// + /// Represents class to determine .NET Framework version difference + /// + public static class FrameworkHelper + { + /// + /// Version of WPF + /// + public static readonly Version PresentationFrameworkVersion = Assembly.GetAssembly(typeof(Window)).GetName().Version; + + /// + /// Gets UseLayoutRounding attached property value + /// + /// + /// + public static bool GetUseLayoutRounding(DependencyObject obj) + { + return (bool)obj.GetValue(UseLayoutRoundingProperty); + } + + /// + /// Gets UseLayoutRounding attached property value + /// + /// + /// + public static void SetUseLayoutRounding(DependencyObject obj, bool value) + { + obj.SetValue(UseLayoutRoundingProperty, value); + } + + /// + /// Using a DependencyProperty as the backing store for UseLayoutRounding. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty UseLayoutRoundingProperty = + DependencyProperty.RegisterAttached("UseLayoutRounding", typeof(bool), typeof(FrameworkHelper), new UIPropertyMetadata(false, OnUseLayoutRoundingChanged)); + + private static void OnUseLayoutRoundingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + d.SetValue(UIElement.SnapsToDevicePixelsProperty, true); + RenderOptions.SetClearTypeHint(d, ClearTypeHint.Enabled); + d.SetValue(FrameworkElement.UseLayoutRoundingProperty, true); + } + + } } \ No newline at end of file diff --git a/Fluent/Helpers/ToggleButtonHelper.cs b/Fluent.Ribbon/Helpers/ToggleButtonHelper.cs similarity index 97% rename from Fluent/Helpers/ToggleButtonHelper.cs rename to Fluent.Ribbon/Helpers/ToggleButtonHelper.cs index ab911d899..cec9923a3 100644 --- a/Fluent/Helpers/ToggleButtonHelper.cs +++ b/Fluent.Ribbon/Helpers/ToggleButtonHelper.cs @@ -1,146 +1,146 @@ -namespace Fluent -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Windows; - - /// - /// Helper-Class for switching states in ToggleButton-Groups - /// - public class ToggleButtonHelper - { - // Grouped buttons - private static readonly Dictionary> groupedButtons = new Dictionary>(); - - /// - /// Handles changes to - /// - public static void OnGroupNameChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var toggleButton = (IToggleButton)d; - var currentGroupName = (string)e.NewValue; - var previousGroupName = (string)e.OldValue; - - if (previousGroupName != null) - { - RemoveFromGroup(previousGroupName, toggleButton); - } - - if (currentGroupName != null) - { - AddToGroup(currentGroupName, toggleButton); - } - } - - /// - /// Coerce - /// - public static object CoerceIsChecked(DependencyObject d, object basevalue) - { - var toggleButton = (IToggleButton)d; - - // If the button does not belong to any group - // or the button/control is not loaded - // we don't have to do any checks and can directly return the requested basevalue - if (toggleButton.GroupName == null - || toggleButton.IsLoaded == false) - { - return basevalue; - } - - var baseIsChecked = (bool?)basevalue; - - if (baseIsChecked.HasValue == false - || baseIsChecked.Value == false) - { - var buttons = GetButtonsInGroup(toggleButton.GroupName); - - // We can not allow that there is no button checked - foreach (var item in buttons) - { - // It's Ok, atleast one checked button exists - // and it's not the current button - if (ReferenceEquals(item, toggleButton) == false - && item.IsChecked == true) - { - return basevalue; - } - } - - // This button can not be unchecked - return true; - } - - return basevalue; - } - - /// - /// Handles changes to - /// - public static void OnIsCheckedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - var newValue = (bool?)e.NewValue; - var button = (IToggleButton)d; - - // Uncheck other toggle buttons - if (!newValue.HasValue || !newValue.Value || button.GroupName == null) - { - return; - } - - var buttons = GetButtonsInGroup(button.GroupName); - - foreach (var item in buttons.Where(item => item != button)) - { - item.IsChecked = false; - } - } - - /// - /// Remove from group - /// - private static void RemoveFromGroup(string groupName, IToggleButton toggleButton) - { - List buttons; - - if (!groupedButtons.TryGetValue(groupName, out buttons)) - { - return; - } - - buttons.RemoveAt(buttons.FindIndex(x => (x.IsAlive && ((IToggleButton)x.Target) == toggleButton))); - } - - /// - /// Add to group - /// - private static void AddToGroup(string groupName, IToggleButton toggleButton) - { - List buttons; - - if (!groupedButtons.TryGetValue(groupName, out buttons)) - { - buttons = new List(); - groupedButtons.Add(groupName, buttons); - } - - buttons.Add(new WeakReference(toggleButton)); - } - - /// - /// Gets all buttons in the given group - /// - private static IEnumerable GetButtonsInGroup(string groupName) - { - List buttons; - - if (!groupedButtons.TryGetValue(groupName, out buttons)) - { - return new List(); - } - - return buttons.Where(x => x.IsAlive).Select(x => (IToggleButton)x.Target).ToList(); - } - } +namespace Fluent +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Windows; + + /// + /// Helper-Class for switching states in ToggleButton-Groups + /// + public class ToggleButtonHelper + { + // Grouped buttons + private static readonly Dictionary> groupedButtons = new Dictionary>(); + + /// + /// Handles changes to + /// + public static void OnGroupNameChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var toggleButton = (IToggleButton)d; + var currentGroupName = (string)e.NewValue; + var previousGroupName = (string)e.OldValue; + + if (previousGroupName != null) + { + RemoveFromGroup(previousGroupName, toggleButton); + } + + if (currentGroupName != null) + { + AddToGroup(currentGroupName, toggleButton); + } + } + + /// + /// Coerce + /// + public static object CoerceIsChecked(DependencyObject d, object basevalue) + { + var toggleButton = (IToggleButton)d; + + // If the button does not belong to any group + // or the button/control is not loaded + // we don't have to do any checks and can directly return the requested basevalue + if (toggleButton.GroupName == null + || toggleButton.IsLoaded == false) + { + return basevalue; + } + + var baseIsChecked = (bool?)basevalue; + + if (baseIsChecked.HasValue == false + || baseIsChecked.Value == false) + { + var buttons = GetButtonsInGroup(toggleButton.GroupName); + + // We can not allow that there is no button checked + foreach (var item in buttons) + { + // It's Ok, atleast one checked button exists + // and it's not the current button + if (ReferenceEquals(item, toggleButton) == false + && item.IsChecked == true) + { + return basevalue; + } + } + + // This button can not be unchecked + return true; + } + + return basevalue; + } + + /// + /// Handles changes to + /// + public static void OnIsCheckedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var newValue = (bool?)e.NewValue; + var button = (IToggleButton)d; + + // Uncheck other toggle buttons + if (!newValue.HasValue || !newValue.Value || button.GroupName == null) + { + return; + } + + var buttons = GetButtonsInGroup(button.GroupName); + + foreach (var item in buttons.Where(item => item != button)) + { + item.IsChecked = false; + } + } + + /// + /// Remove from group + /// + private static void RemoveFromGroup(string groupName, IToggleButton toggleButton) + { + List buttons; + + if (!groupedButtons.TryGetValue(groupName, out buttons)) + { + return; + } + + buttons.RemoveAt(buttons.FindIndex(x => (x.IsAlive && ((IToggleButton)x.Target) == toggleButton))); + } + + /// + /// Add to group + /// + private static void AddToGroup(string groupName, IToggleButton toggleButton) + { + List buttons; + + if (!groupedButtons.TryGetValue(groupName, out buttons)) + { + buttons = new List(); + groupedButtons.Add(groupName, buttons); + } + + buttons.Add(new WeakReference(toggleButton)); + } + + /// + /// Gets all buttons in the given group + /// + private static IEnumerable GetButtonsInGroup(string groupName) + { + List buttons; + + if (!groupedButtons.TryGetValue(groupName, out buttons)) + { + return new List(); + } + + return buttons.Where(x => x.IsAlive).Select(x => (IToggleButton)x.Target).ToList(); + } + } } \ No newline at end of file diff --git a/Fluent/IDropDownControl.cs b/Fluent.Ribbon/IDropDownControl.cs similarity index 96% rename from Fluent/IDropDownControl.cs rename to Fluent.Ribbon/IDropDownControl.cs index 6253cb2b6..fda743bf9 100644 --- a/Fluent/IDropDownControl.cs +++ b/Fluent.Ribbon/IDropDownControl.cs @@ -1,25 +1,25 @@ -namespace Fluent -{ - using System.Windows.Controls.Primitives; - - /// - /// Represents control that have drop down popup - /// - public interface IDropDownControl - { - /// - /// Gets drop down popup - /// - Popup DropDownPopup { get; } - - /// - /// Gets a value indicating whether control context menu is opened - /// - bool IsContextMenuOpened { get; set; } - - /// - /// Gets or sets a value indicating whether drop down is opened - /// - bool IsDropDownOpen { get; set; } - } +namespace Fluent +{ + using System.Windows.Controls.Primitives; + + /// + /// Represents control that have drop down popup + /// + public interface IDropDownControl + { + /// + /// Gets drop down popup + /// + Popup DropDownPopup { get; } + + /// + /// Gets a value indicating whether control context menu is opened + /// + bool IsContextMenuOpened { get; set; } + + /// + /// Gets or sets a value indicating whether drop down is opened + /// + bool IsDropDownOpen { get; set; } + } } \ No newline at end of file diff --git a/Fluent/IHeaderedControl.cs b/Fluent.Ribbon/IHeaderedControl.cs similarity index 100% rename from Fluent/IHeaderedControl.cs rename to Fluent.Ribbon/IHeaderedControl.cs diff --git a/Fluent/IKeyTipedControl.cs b/Fluent.Ribbon/IKeyTipedControl.cs similarity index 96% rename from Fluent/IKeyTipedControl.cs rename to Fluent.Ribbon/IKeyTipedControl.cs index bd2217ec4..8ad6f5d01 100644 --- a/Fluent/IKeyTipedControl.cs +++ b/Fluent.Ribbon/IKeyTipedControl.cs @@ -1,23 +1,23 @@ -namespace Fluent -{ - /// - /// Base interface for controls supports key tips - /// - public interface IKeyTipedControl - { - /// - /// Get and sets KeyTip for element. - /// - string KeyTip { get; set; } - - /// - /// Handles key tip pressed - /// - void OnKeyTipPressed(); - - /// - /// Handles back navigation with KeyTips - /// - void OnKeyTipBack(); - } +namespace Fluent +{ + /// + /// Base interface for controls supports key tips + /// + public interface IKeyTipedControl + { + /// + /// Get and sets KeyTip for element. + /// + string KeyTip { get; set; } + + /// + /// Handles key tip pressed + /// + void OnKeyTipPressed(); + + /// + /// Handles back navigation with KeyTips + /// + void OnKeyTipBack(); + } } \ No newline at end of file diff --git a/Fluent/IRibbonControl.cs b/Fluent.Ribbon/IRibbonControl.cs similarity index 96% rename from Fluent/IRibbonControl.cs rename to Fluent.Ribbon/IRibbonControl.cs index 25567d275..d3dee5eda 100644 --- a/Fluent/IRibbonControl.cs +++ b/Fluent.Ribbon/IRibbonControl.cs @@ -1,23 +1,23 @@ -namespace Fluent -{ - /// - /// Base interface for Fluent controls - /// - public interface IRibbonControl : IHeaderedControl, IKeyTipedControl - { - /// - /// Gets or sets Size for the element - /// - RibbonControlSize Size { get; set; } - - /// - /// Gets or sets SizeDefinition for element - /// - RibbonControlSizeDefinition SizeDefinition { get; set; } - - /// - /// Gets or sets Icon for the element - /// - object Icon { get; set; } - } +namespace Fluent +{ + /// + /// Base interface for Fluent controls + /// + public interface IRibbonControl : IHeaderedControl, IKeyTipedControl + { + /// + /// Gets or sets Size for the element + /// + RibbonControlSize Size { get; set; } + + /// + /// Gets or sets SizeDefinition for element + /// + RibbonControlSizeDefinition SizeDefinition { get; set; } + + /// + /// Gets or sets Icon for the element + /// + object Icon { get; set; } + } } \ No newline at end of file diff --git a/Fluent/IScalableRibbonControl.cs b/Fluent.Ribbon/IScalableRibbonControl.cs similarity index 95% rename from Fluent/IScalableRibbonControl.cs rename to Fluent.Ribbon/IScalableRibbonControl.cs index 2d665c0fe..b6caa493e 100644 --- a/Fluent/IScalableRibbonControl.cs +++ b/Fluent.Ribbon/IScalableRibbonControl.cs @@ -1,24 +1,24 @@ -using System; - -namespace Fluent -{ - /// - /// Repesents scalable ribbon contol - /// - public interface IScalableRibbonControl - { - /// - /// Enlarge control size - /// - void Enlarge(); - /// - /// Reduce control size - /// - void Reduce(); - - /// - /// Occurs when contol is scaled - /// - event EventHandler Scaled; - } -} +using System; + +namespace Fluent +{ + /// + /// Repesents scalable ribbon contol + /// + public interface IScalableRibbonControl + { + /// + /// Enlarge control size + /// + void Enlarge(); + /// + /// Reduce control size + /// + void Reduce(); + + /// + /// Occurs when contol is scaled + /// + event EventHandler Scaled; + } +} diff --git a/Fluent/IToggleButton.cs b/Fluent.Ribbon/IToggleButton.cs similarity index 97% rename from Fluent/IToggleButton.cs rename to Fluent.Ribbon/IToggleButton.cs index 4d918d83a..d7176ab7c 100644 --- a/Fluent/IToggleButton.cs +++ b/Fluent.Ribbon/IToggleButton.cs @@ -1,28 +1,28 @@ -namespace Fluent -{ - /// - /// Interface for controls that support -Behavior - /// - public interface IToggleButton - { - /// - /// Gets or sets the name of the group that the toggle button belongs to. - /// Use the GroupName property to specify a grouping of toggle buttons to - /// create a mutually exclusive set of controls. You can use the GroupName - /// property when only one selection is possible from a list of available - /// options. When this property is set, only one ToggleButton in the specified - /// group can be selected at a time. - /// - string GroupName { get; set; } - - /// - /// Gets or sets a value indicating whether SplitButton is checked - /// - bool? IsChecked { get; set; } - - /// - /// Gets a value that indicates whether the ToggleButton is fully loaded - /// - bool IsLoaded { get; } - } +namespace Fluent +{ + /// + /// Interface for controls that support -Behavior + /// + public interface IToggleButton + { + /// + /// Gets or sets the name of the group that the toggle button belongs to. + /// Use the GroupName property to specify a grouping of toggle buttons to + /// create a mutually exclusive set of controls. You can use the GroupName + /// property when only one selection is possible from a list of available + /// options. When this property is set, only one ToggleButton in the specified + /// group can be selected at a time. + /// + string GroupName { get; set; } + + /// + /// Gets or sets a value indicating whether SplitButton is checked + /// + bool? IsChecked { get; set; } + + /// + /// Gets a value that indicates whether the ToggleButton is fully loaded + /// + bool IsLoaded { get; } + } } \ No newline at end of file diff --git a/Fluent/Images/DefaultSmallIcon.png b/Fluent.Ribbon/Images/DefaultSmallIcon.png similarity index 100% rename from Fluent/Images/DefaultSmallIcon.png rename to Fluent.Ribbon/Images/DefaultSmallIcon.png diff --git a/Fluent/Internal/CommandHelper.cs b/Fluent.Ribbon/Internal/CommandHelper.cs similarity index 96% rename from Fluent/Internal/CommandHelper.cs rename to Fluent.Ribbon/Internal/CommandHelper.cs index a1b4aea8c..7278823cb 100644 --- a/Fluent/Internal/CommandHelper.cs +++ b/Fluent.Ribbon/Internal/CommandHelper.cs @@ -1,58 +1,58 @@ -namespace Fluent.Internal -{ - using System.Windows; - using System.Windows.Input; - - /// - /// Helper class for - /// - public static class CommandHelper - { - /// - /// Checks if can be executed. - /// This method is null safe. - /// - /// true if the command can be executed, otherwise false. - public static bool CanExecute(ICommand command, object commandParameter, IInputElement commandTarget) - { - if (command == null) - { - return false; - } - - var routedCommand = command as RoutedCommand; - - if (routedCommand != null) - { - return routedCommand.CanExecute(commandParameter, commandTarget); - } - - return command.CanExecute(commandParameter); ; - } - - /// - /// Executes . - /// This method is null safe. - /// - public static void Execute(ICommand command, object commandParameter, IInputElement commandTarget) - { - if (command == null) - { - return; - } - - var routedCommand = command as RoutedCommand; - if (routedCommand != null) - { - if (routedCommand.CanExecute(commandParameter, commandTarget)) - { - routedCommand.Execute(commandParameter, commandTarget); - } - } - else if (command.CanExecute(commandParameter)) - { - command.Execute(commandParameter); - } - } - } +namespace Fluent.Internal +{ + using System.Windows; + using System.Windows.Input; + + /// + /// Helper class for + /// + public static class CommandHelper + { + /// + /// Checks if can be executed. + /// This method is null safe. + /// + /// true if the command can be executed, otherwise false. + public static bool CanExecute(ICommand command, object commandParameter, IInputElement commandTarget) + { + if (command == null) + { + return false; + } + + var routedCommand = command as RoutedCommand; + + if (routedCommand != null) + { + return routedCommand.CanExecute(commandParameter, commandTarget); + } + + return command.CanExecute(commandParameter); ; + } + + /// + /// Executes . + /// This method is null safe. + /// + public static void Execute(ICommand command, object commandParameter, IInputElement commandTarget) + { + if (command == null) + { + return; + } + + var routedCommand = command as RoutedCommand; + if (routedCommand != null) + { + if (routedCommand.CanExecute(commandParameter, commandTarget)) + { + routedCommand.Execute(commandParameter, commandTarget); + } + } + else if (command.CanExecute(commandParameter)) + { + command.Execute(commandParameter); + } + } + } } \ No newline at end of file diff --git a/Fluent/Internal/DoubleUtil.cs b/Fluent.Ribbon/Internal/DoubleUtil.cs similarity index 97% rename from Fluent/Internal/DoubleUtil.cs rename to Fluent.Ribbon/Internal/DoubleUtil.cs index 87d03e438..5ec7d546a 100644 --- a/Fluent/Internal/DoubleUtil.cs +++ b/Fluent.Ribbon/Internal/DoubleUtil.cs @@ -1,39 +1,39 @@ -namespace Fluent.Internal -{ - using System; - - internal static class DoubleUtil - { - // Const values come from sdk\inc\crt\float.h - internal const double DBL_EPSILON = 2.2204460492503131e-016; /* smallest such that 1.0+DBL_EPSILON != 1.0 */ - - /// - /// AreClose - Returns whether or not two doubles are "close". That is, whether or - /// not they are within epsilon of each other. Note that this epsilon is proportional - /// to the numbers themselves to that AreClose survives scalar multiplication. - /// There are plenty of ways for this to return false even for numbers which - /// are theoretically identical, so no code calling this should fail to work if this - /// returns false. This is important enough to repeat: - /// NB: NO CODE CALLING THIS FUNCTION SHOULD DEPEND ON ACCURATE RESULTS - this should be - /// used for optimizations *only*. - /// - /// - /// bool - the result of the AreClose comparision. - /// - /// The first double to compare. - /// The second double to compare. - public static bool AreClose(double value1, double value2) - { - // in case they are Infinities (then epsilon check does not work) - if (value1 == value2) - { - return true; - } - - // This computes (|value1-value2| / (|value1| + |value2| + 10.0)) < DBL_EPSILON - var eps = (Math.Abs(value1) + Math.Abs(value2) + 10.0) * DBL_EPSILON; - var delta = value1 - value2; - return (-eps < delta) && (eps > delta); - } - } +namespace Fluent.Internal +{ + using System; + + internal static class DoubleUtil + { + // Const values come from sdk\inc\crt\float.h + internal const double DBL_EPSILON = 2.2204460492503131e-016; /* smallest such that 1.0+DBL_EPSILON != 1.0 */ + + /// + /// AreClose - Returns whether or not two doubles are "close". That is, whether or + /// not they are within epsilon of each other. Note that this epsilon is proportional + /// to the numbers themselves to that AreClose survives scalar multiplication. + /// There are plenty of ways for this to return false even for numbers which + /// are theoretically identical, so no code calling this should fail to work if this + /// returns false. This is important enough to repeat: + /// NB: NO CODE CALLING THIS FUNCTION SHOULD DEPEND ON ACCURATE RESULTS - this should be + /// used for optimizations *only*. + /// + /// + /// bool - the result of the AreClose comparision. + /// + /// The first double to compare. + /// The second double to compare. + public static bool AreClose(double value1, double value2) + { + // in case they are Infinities (then epsilon check does not work) + if (value1 == value2) + { + return true; + } + + // This computes (|value1-value2| / (|value1| + |value2| + 10.0)) < DBL_EPSILON + var eps = (Math.Abs(value1) + Math.Abs(value2) + 10.0) * DBL_EPSILON; + var delta = value1 - value2; + return (-eps < delta) && (eps > delta); + } + } } \ No newline at end of file diff --git a/Fluent/Internal/InvokeCommand.cs b/Fluent.Ribbon/Internal/InvokeCommand.cs similarity index 97% rename from Fluent/Internal/InvokeCommand.cs rename to Fluent.Ribbon/Internal/InvokeCommand.cs index 3a2c5cac3..2411456b4 100644 --- a/Fluent/Internal/InvokeCommand.cs +++ b/Fluent.Ribbon/Internal/InvokeCommand.cs @@ -1,66 +1,66 @@ -namespace Fluent.Internal -{ - using System.Windows; - using System.Windows.Controls.Primitives; - using System.Windows.Data; - using System.Windows.Input; - using System.Windows.Interactivity; - - /// - /// This trigger action binds a command/command parameter for MVVM usage with - /// a Blend based trigger. This is used in place of the one in the Blend samples - - /// it has a problem in it as of the current (first) release. Once it is fixed, this - /// command can go away. - /// - [DefaultTrigger(typeof(ButtonBase), typeof(System.Windows.Interactivity.EventTrigger), "Click")] - [DefaultTrigger(typeof(UIElement), typeof(System.Windows.Interactivity.EventTrigger), "MouseLeftButtonDown")] - public class InvokeCommand : TriggerAction - { - /// - /// ICommand to execute - /// - public static readonly DependencyProperty CommandProperty = - DependencyProperty.Register("Command", typeof(ICommand), typeof(InvokeCommand), new PropertyMetadata(null)); - - /// - /// Command parameter to pass to command execution - /// - public static readonly DependencyProperty CommandParameterProperty = - DependencyProperty.Register("CommandParameter", typeof(object), typeof(InvokeCommand), new PropertyMetadata(null)); - - /// - /// Command to execute - /// - public ICommand Command - { - get { return (ICommand)this.GetValue(CommandProperty); } - set { this.SetValue(CommandProperty, value); } - } - - /// - /// Command parameter - /// - public object CommandParameter - { - get { return this.GetValue(CommandParameterProperty); } - set { this.SetValue(CommandParameterProperty, value); } - } - - /// - /// This is called to execute the command when the trigger conditions are satisfied. - /// - /// parameter (not used) - protected override void Invoke(object parameter) - { - var commandParameter = BindingOperations.IsDataBound(this, CommandParameterProperty) - ? this.CommandParameter - : parameter; - var command = this.Command; - - if ((command != null) && command.CanExecute(commandParameter)) - { - command.Execute(commandParameter); - } - } - } -} +namespace Fluent.Internal +{ + using System.Windows; + using System.Windows.Controls.Primitives; + using System.Windows.Data; + using System.Windows.Input; + using System.Windows.Interactivity; + + /// + /// This trigger action binds a command/command parameter for MVVM usage with + /// a Blend based trigger. This is used in place of the one in the Blend samples - + /// it has a problem in it as of the current (first) release. Once it is fixed, this + /// command can go away. + /// + [DefaultTrigger(typeof(ButtonBase), typeof(System.Windows.Interactivity.EventTrigger), "Click")] + [DefaultTrigger(typeof(UIElement), typeof(System.Windows.Interactivity.EventTrigger), "MouseLeftButtonDown")] + public class InvokeCommand : TriggerAction + { + /// + /// ICommand to execute + /// + public static readonly DependencyProperty CommandProperty = + DependencyProperty.Register("Command", typeof(ICommand), typeof(InvokeCommand), new PropertyMetadata(null)); + + /// + /// Command parameter to pass to command execution + /// + public static readonly DependencyProperty CommandParameterProperty = + DependencyProperty.Register("CommandParameter", typeof(object), typeof(InvokeCommand), new PropertyMetadata(null)); + + /// + /// Command to execute + /// + public ICommand Command + { + get { return (ICommand)this.GetValue(CommandProperty); } + set { this.SetValue(CommandProperty, value); } + } + + /// + /// Command parameter + /// + public object CommandParameter + { + get { return this.GetValue(CommandParameterProperty); } + set { this.SetValue(CommandParameterProperty, value); } + } + + /// + /// This is called to execute the command when the trigger conditions are satisfied. + /// + /// parameter (not used) + protected override void Invoke(object parameter) + { + var commandParameter = BindingOperations.IsDataBound(this, CommandParameterProperty) + ? this.CommandParameter + : parameter; + var command = this.Command; + + if ((command != null) && command.CanExecute(commandParameter)) + { + command.Execute(commandParameter); + } + } + } +} diff --git a/Fluent/Internal/ItemContainerGeneratorAction.cs b/Fluent.Ribbon/Internal/ItemContainerGeneratorAction.cs similarity index 97% rename from Fluent/Internal/ItemContainerGeneratorAction.cs rename to Fluent.Ribbon/Internal/ItemContainerGeneratorAction.cs index 8d73b7db5..04b2c93c7 100644 --- a/Fluent/Internal/ItemContainerGeneratorAction.cs +++ b/Fluent.Ribbon/Internal/ItemContainerGeneratorAction.cs @@ -1,66 +1,66 @@ -namespace Fluent.Internal -{ - using System; - using System.Windows.Controls; - using System.Windows.Controls.Primitives; - - /// - /// Helper class used to queue action for completion or items changes of - /// - internal class ItemContainerGeneratorAction - { - /// - /// Creates a new instance used to queue action for completion or items changes of - /// - /// The to be used. - /// The that should be invoked. - public ItemContainerGeneratorAction(ItemContainerGenerator generator, Action action) - { - this.Generator = generator; - this.Action = action; - } - - /// - /// Gets the to be used. - /// - public ItemContainerGenerator Generator { get; private set; } - - /// - /// Gets the that should be invoked. - /// - public Action Action { get; private set; } - - /// - /// Gets the current wait state. true in case was called and we are waiting for the to finish. - /// - public bool IsWaitingForGenerator { get; private set; } - - /// - /// Queues for invocation. - /// - public void QueueAction() - { - if (this.Generator.Status != GeneratorStatus.ContainersGenerated) - { - if (this.IsWaitingForGenerator) - { - return; - } - - this.IsWaitingForGenerator = true; - this.Generator.StatusChanged += this.HandleItemContainerGenerator_StatusChanged; - return; - } - - this.IsWaitingForGenerator = false; - this.Generator.StatusChanged -= this.HandleItemContainerGenerator_StatusChanged; - - this.Action(); - } - - private void HandleItemContainerGenerator_StatusChanged(object sender, EventArgs e) - { - this.QueueAction(); - } - } +namespace Fluent.Internal +{ + using System; + using System.Windows.Controls; + using System.Windows.Controls.Primitives; + + /// + /// Helper class used to queue action for completion or items changes of + /// + internal class ItemContainerGeneratorAction + { + /// + /// Creates a new instance used to queue action for completion or items changes of + /// + /// The to be used. + /// The that should be invoked. + public ItemContainerGeneratorAction(ItemContainerGenerator generator, Action action) + { + this.Generator = generator; + this.Action = action; + } + + /// + /// Gets the to be used. + /// + public ItemContainerGenerator Generator { get; private set; } + + /// + /// Gets the that should be invoked. + /// + public Action Action { get; private set; } + + /// + /// Gets the current wait state. true in case was called and we are waiting for the to finish. + /// + public bool IsWaitingForGenerator { get; private set; } + + /// + /// Queues for invocation. + /// + public void QueueAction() + { + if (this.Generator.Status != GeneratorStatus.ContainersGenerated) + { + if (this.IsWaitingForGenerator) + { + return; + } + + this.IsWaitingForGenerator = true; + this.Generator.StatusChanged += this.HandleItemContainerGenerator_StatusChanged; + return; + } + + this.IsWaitingForGenerator = false; + this.Generator.StatusChanged -= this.HandleItemContainerGenerator_StatusChanged; + + this.Action(); + } + + private void HandleItemContainerGenerator_StatusChanged(object sender, EventArgs e) + { + this.QueueAction(); + } + } } \ No newline at end of file diff --git a/Fluent/Internal/ItemsControlHelper.cs b/Fluent.Ribbon/Internal/ItemsControlHelper.cs similarity index 100% rename from Fluent/Internal/ItemsControlHelper.cs rename to Fluent.Ribbon/Internal/ItemsControlHelper.cs diff --git a/Fluent/Internal/NativeMethods.cs b/Fluent.Ribbon/Internal/NativeMethods.cs similarity index 98% rename from Fluent/Internal/NativeMethods.cs rename to Fluent.Ribbon/Internal/NativeMethods.cs index 1e61bc3f2..2e7991c05 100644 --- a/Fluent/Internal/NativeMethods.cs +++ b/Fluent.Ribbon/Internal/NativeMethods.cs @@ -1,136 +1,136 @@ -using System; -using System.Runtime.InteropServices; - -namespace Fluent -{ - using Fluent.Metro.Native; - - internal static class NativeMethods - { - /// - /// Causes the dialog box to display all available colors in the set of basic colors. - /// - public const int CC_ANYCOLOR = 0x00000100; - - /// - /// The MonitorFromRect function retrieves a handle to the display monitor that - /// has the largest area of intersection with a specified rectangle. - /// - /// Pointer to a RECT structure that specifies the rectangle of interest in - /// virtual-screen coordinates - /// Determines the function's return value if the rectangle does not intersect - /// any display monitor - /// - /// If the rectangle intersects one or more display monitor rectangles, the return value - /// is an HMONITOR handle to the display monitor that has the largest area of intersection with the rectangle. - /// If the rectangle does not intersect a display monitor, the return value depends on the value of dwFlags. - /// - [DllImport("user32.dll")] - public static extern IntPtr MonitorFromRect([In] ref RECT lprc, uint dwFlags); - - /// - /// Loads an icon, cursor, animated cursor, or bitmap. - /// - /// Handle to the module of either a DLL or executable (.exe) that contains the image to be loaded - /// Specifies the image to load - /// Specifies the type of image to be loaded. - /// Specifies the width, in pixels, of the icon or cursor - /// Specifies the height, in pixels, of the icon or cursor - /// This parameter can be one or more of the following values. - /// If the function succeeds, the return value is the requested value.If the function fails, the return value is zero. To get extended error information, call GetLastError. - [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] - public static extern IntPtr LoadImage(IntPtr hinst, IntPtr lpszName, uint uType, int cxDesired, int cyDesired, uint fuLoad); - - /// - /// Creates a Color dialog box that enables the user to select a color. - /// - /// A pointer to a CHOOSECOLOR structure that contains information used to initialize the dialog box. When ChooseColor returns, this structure contains information about the user's color selection. - /// If the user clicks the OK button of the dialog box, the return value is nonzero. The rgbResult member of the CHOOSECOLOR structure contains the RGB color value of the color selected by the user.If the user cancels or closes the Color dialog box or an error occurs, the return value is zero. - [DllImport("comdlg32.dll")] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool ChooseColor(CHOOSECOLOR lpcc); - - /// - /// Contains information the ChooseColor function uses to initialize the Color dialog box. After the user closes the dialog box, the system returns information about the user's selection in this structure. - /// - [StructLayout(LayoutKind.Sequential)] - public class CHOOSECOLOR - { - /// - /// The length, in bytes, of the structure. - /// - public int lStructSize = Marshal.SizeOf(typeof(CHOOSECOLOR)); - /// - /// A handle to the window that owns the dialog box. This member can be any valid window handle, or it can be NULL if the dialog box has no owner. - /// - public IntPtr hwndOwner; - /// - /// If the CC_ENABLETEMPLATEHANDLE flag is set in the Flags member, hInstance is a handle to a memory object containing a dialog box template. If the CC_ENABLETEMPLATE flag is set, hInstance is a handle to a module that contains a dialog box template named by the lpTemplateName member. If neither CC_ENABLETEMPLATEHANDLE nor CC_ENABLETEMPLATE is set, this member is ignored. - /// - public IntPtr hInstance = IntPtr.Zero; - /// - /// If the CC_RGBINIT flag is set, rgbResult specifies the color initially selected when the dialog box is created. If the specified color value is not among the available colors, the system selects the nearest solid color available. If rgbResult is zero or CC_RGBINIT is not set, the initially selected color is black. If the user clicks the OK button, rgbResult specifies the user's color selection. To create a COLORREF color value, use the RGB macro. - /// - public int rgbResult; - /// - /// A pointer to an array of 16 values that contain red, green, blue (RGB) values for the custom color boxes in the dialog box. If the user modifies these colors, the system updates the array with the new RGB values. To preserve new custom colors between calls to the ChooseColor function, you should allocate static memory for the array. To create a COLORREF color value, use the RGB macro. - /// - public IntPtr lpCustColors = IntPtr.Zero; - /// - /// A set of bit flags that you can use to initialize the Color dialog box. When the dialog box returns, it sets these flags to indicate the user's input. - /// - public int Flags; - /// - /// Application-defined data that the system passes to the hook procedure identified by the lpfnHook member. When the system sends the WM_INITDIALOG message to the hook procedure, the message's lParam parameter is a pointer to the CHOOSECOLOR structure specified when the dialog was created. The hook procedure can use this pointer to get the lCustData value. - /// - public IntPtr lCustData = IntPtr.Zero; - /// - /// A pointer to a CCHookProc hook procedure that can process messages intended for the dialog box. This member is ignored unless the CC_ENABLEHOOK flag is set in the Flags member. - /// - public IntPtr lpfnHook = IntPtr.Zero; - /// - /// The name of the dialog box template resource in the module identified by the hInstance member. This template is substituted for the standard dialog box template. For numbered dialog box resources, lpTemplateName can be a value returned by the MAKEINTRESOURCE macro. This member is ignored unless the CC_ENABLETEMPLATE flag is set in the Flags member. - /// - public IntPtr lpTemplateName = IntPtr.Zero; - } - - // Sets in first IsDwmEnabled call - private static bool idDwmDllNotFound; - - /// - /// Is DWM enabled - /// - /// Is DWM enabled - public static bool IsDwmEnabled() - { - if (idDwmDllNotFound) return false; - - if (Environment.OSVersion.Version.Major < 6) - { - idDwmDllNotFound = true; - return false; - } - - try - { - return UnsafeNativeMethods.DwmIsCompositionEnabled(); - } - catch (DllNotFoundException) - { - idDwmDllNotFound = true; - return false; - } - } - - /// - /// Sends a message to the message window and waits until the WndProc method has processed the message. - /// - /// Handle to destination window - /// Message - /// First message parameter - /// Second message parameter - /// - [DllImport("user32.dll")] - public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam); - } +using System; +using System.Runtime.InteropServices; + +namespace Fluent +{ + using Fluent.Metro.Native; + + internal static class NativeMethods + { + /// + /// Causes the dialog box to display all available colors in the set of basic colors. + /// + public const int CC_ANYCOLOR = 0x00000100; + + /// + /// The MonitorFromRect function retrieves a handle to the display monitor that + /// has the largest area of intersection with a specified rectangle. + /// + /// Pointer to a RECT structure that specifies the rectangle of interest in + /// virtual-screen coordinates + /// Determines the function's return value if the rectangle does not intersect + /// any display monitor + /// + /// If the rectangle intersects one or more display monitor rectangles, the return value + /// is an HMONITOR handle to the display monitor that has the largest area of intersection with the rectangle. + /// If the rectangle does not intersect a display monitor, the return value depends on the value of dwFlags. + /// + [DllImport("user32.dll")] + public static extern IntPtr MonitorFromRect([In] ref RECT lprc, uint dwFlags); + + /// + /// Loads an icon, cursor, animated cursor, or bitmap. + /// + /// Handle to the module of either a DLL or executable (.exe) that contains the image to be loaded + /// Specifies the image to load + /// Specifies the type of image to be loaded. + /// Specifies the width, in pixels, of the icon or cursor + /// Specifies the height, in pixels, of the icon or cursor + /// This parameter can be one or more of the following values. + /// If the function succeeds, the return value is the requested value.If the function fails, the return value is zero. To get extended error information, call GetLastError. + [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] + public static extern IntPtr LoadImage(IntPtr hinst, IntPtr lpszName, uint uType, int cxDesired, int cyDesired, uint fuLoad); + + /// + /// Creates a Color dialog box that enables the user to select a color. + /// + /// A pointer to a CHOOSECOLOR structure that contains information used to initialize the dialog box. When ChooseColor returns, this structure contains information about the user's color selection. + /// If the user clicks the OK button of the dialog box, the return value is nonzero. The rgbResult member of the CHOOSECOLOR structure contains the RGB color value of the color selected by the user.If the user cancels or closes the Color dialog box or an error occurs, the return value is zero. + [DllImport("comdlg32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool ChooseColor(CHOOSECOLOR lpcc); + + /// + /// Contains information the ChooseColor function uses to initialize the Color dialog box. After the user closes the dialog box, the system returns information about the user's selection in this structure. + /// + [StructLayout(LayoutKind.Sequential)] + public class CHOOSECOLOR + { + /// + /// The length, in bytes, of the structure. + /// + public int lStructSize = Marshal.SizeOf(typeof(CHOOSECOLOR)); + /// + /// A handle to the window that owns the dialog box. This member can be any valid window handle, or it can be NULL if the dialog box has no owner. + /// + public IntPtr hwndOwner; + /// + /// If the CC_ENABLETEMPLATEHANDLE flag is set in the Flags member, hInstance is a handle to a memory object containing a dialog box template. If the CC_ENABLETEMPLATE flag is set, hInstance is a handle to a module that contains a dialog box template named by the lpTemplateName member. If neither CC_ENABLETEMPLATEHANDLE nor CC_ENABLETEMPLATE is set, this member is ignored. + /// + public IntPtr hInstance = IntPtr.Zero; + /// + /// If the CC_RGBINIT flag is set, rgbResult specifies the color initially selected when the dialog box is created. If the specified color value is not among the available colors, the system selects the nearest solid color available. If rgbResult is zero or CC_RGBINIT is not set, the initially selected color is black. If the user clicks the OK button, rgbResult specifies the user's color selection. To create a COLORREF color value, use the RGB macro. + /// + public int rgbResult; + /// + /// A pointer to an array of 16 values that contain red, green, blue (RGB) values for the custom color boxes in the dialog box. If the user modifies these colors, the system updates the array with the new RGB values. To preserve new custom colors between calls to the ChooseColor function, you should allocate static memory for the array. To create a COLORREF color value, use the RGB macro. + /// + public IntPtr lpCustColors = IntPtr.Zero; + /// + /// A set of bit flags that you can use to initialize the Color dialog box. When the dialog box returns, it sets these flags to indicate the user's input. + /// + public int Flags; + /// + /// Application-defined data that the system passes to the hook procedure identified by the lpfnHook member. When the system sends the WM_INITDIALOG message to the hook procedure, the message's lParam parameter is a pointer to the CHOOSECOLOR structure specified when the dialog was created. The hook procedure can use this pointer to get the lCustData value. + /// + public IntPtr lCustData = IntPtr.Zero; + /// + /// A pointer to a CCHookProc hook procedure that can process messages intended for the dialog box. This member is ignored unless the CC_ENABLEHOOK flag is set in the Flags member. + /// + public IntPtr lpfnHook = IntPtr.Zero; + /// + /// The name of the dialog box template resource in the module identified by the hInstance member. This template is substituted for the standard dialog box template. For numbered dialog box resources, lpTemplateName can be a value returned by the MAKEINTRESOURCE macro. This member is ignored unless the CC_ENABLETEMPLATE flag is set in the Flags member. + /// + public IntPtr lpTemplateName = IntPtr.Zero; + } + + // Sets in first IsDwmEnabled call + private static bool idDwmDllNotFound; + + /// + /// Is DWM enabled + /// + /// Is DWM enabled + public static bool IsDwmEnabled() + { + if (idDwmDllNotFound) return false; + + if (Environment.OSVersion.Version.Major < 6) + { + idDwmDllNotFound = true; + return false; + } + + try + { + return UnsafeNativeMethods.DwmIsCompositionEnabled(); + } + catch (DllNotFoundException) + { + idDwmDllNotFound = true; + return false; + } + } + + /// + /// Sends a message to the message window and waits until the WndProc method has processed the message. + /// + /// Handle to destination window + /// Message + /// First message parameter + /// Second message parameter + /// + [DllImport("user32.dll")] + public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam); + } } \ No newline at end of file diff --git a/Fluent/Internal/UIHelper.cs b/Fluent.Ribbon/Internal/UIHelper.cs similarity index 97% rename from Fluent/Internal/UIHelper.cs rename to Fluent.Ribbon/Internal/UIHelper.cs index f6a0fb2e7..a064fd010 100644 --- a/Fluent/Internal/UIHelper.cs +++ b/Fluent.Ribbon/Internal/UIHelper.cs @@ -1,60 +1,60 @@ -namespace Fluent.Internal -{ - using System; - using System.Windows; - using System.Windows.Media; - - /// - /// Class with helper functions for UI related stuff - /// - internal class UIHelper - { - /// - /// Tries to find immediate visual child of type which matches - /// - /// - /// The visual child of type that matches . - /// Returns null if no child matches. - /// - public static T FindImmediateVisualChild(DependencyObject parent, Predicate predicate) - where T : DependencyObject - { - for (var i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++) - { - var obj = VisualTreeHelper.GetChild(parent, i) as T; - - if (obj != null - && predicate(obj)) - { - return obj; - } - } - - return null; - } - - /// - /// Gets the first visual child of type TChildItem by walking down the visual tree. - /// - /// The type of visual child to find. - /// The parent element whose visual tree shall be walked down. - /// The first element of type TChildItem found in the visual tree is returned. If none is found, null is returned. - public static TChildItem FindVisualChild(DependencyObject obj) where TChildItem : DependencyObject - { - for (var i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++) - { - var child = VisualTreeHelper.GetChild(obj, i); - var item = child as TChildItem; - - if (item != null) - return item; - - var childOfChild = FindVisualChild(child); - if (childOfChild != null) - return childOfChild; - } - return null; - } - - } +namespace Fluent.Internal +{ + using System; + using System.Windows; + using System.Windows.Media; + + /// + /// Class with helper functions for UI related stuff + /// + internal class UIHelper + { + /// + /// Tries to find immediate visual child of type which matches + /// + /// + /// The visual child of type that matches . + /// Returns null if no child matches. + /// + public static T FindImmediateVisualChild(DependencyObject parent, Predicate predicate) + where T : DependencyObject + { + for (var i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++) + { + var obj = VisualTreeHelper.GetChild(parent, i) as T; + + if (obj != null + && predicate(obj)) + { + return obj; + } + } + + return null; + } + + /// + /// Gets the first visual child of type TChildItem by walking down the visual tree. + /// + /// The type of visual child to find. + /// The parent element whose visual tree shall be walked down. + /// The first element of type TChildItem found in the visual tree is returned. If none is found, null is returned. + public static TChildItem FindVisualChild(DependencyObject obj) where TChildItem : DependencyObject + { + for (var i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++) + { + var child = VisualTreeHelper.GetChild(obj, i); + var item = child as TChildItem; + + if (item != null) + return item; + + var childOfChild = FindVisualChild(child); + if (childOfChild != null) + return childOfChild; + } + return null; + } + + } } \ No newline at end of file diff --git a/Fluent/Metro/Behaviours/StylizedBehaviors.cs b/Fluent.Ribbon/Metro/Behaviours/StylizedBehaviors.cs similarity index 97% rename from Fluent/Metro/Behaviours/StylizedBehaviors.cs rename to Fluent.Ribbon/Metro/Behaviours/StylizedBehaviors.cs index 7f57c824a..18e450c38 100644 --- a/Fluent/Metro/Behaviours/StylizedBehaviors.cs +++ b/Fluent.Ribbon/Metro/Behaviours/StylizedBehaviors.cs @@ -1,127 +1,127 @@ -using System.Windows; -using System.Windows.Interactivity; - -namespace Fluent.Metro.Behaviours -{ - /// - /// Enables the use of behaviors in styles - /// - public class StylizedBehaviors - { - private static readonly DependencyProperty OriginalBehaviorProperty = DependencyProperty.RegisterAttached(@"OriginalBehaviorInternal", typeof(Behavior), typeof(StylizedBehaviors), new UIPropertyMetadata(null)); - - /// - /// Using a DependencyProperty as the backing store for Behaviors. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty BehaviorsProperty = DependencyProperty.RegisterAttached( - @"Behaviors", - typeof(StylizedBehaviorCollection), - typeof(StylizedBehaviors), - new FrameworkPropertyMetadata(null, OnPropertyChanged)); - - /// - /// Gets Behaviors for element - /// - public static StylizedBehaviorCollection GetBehaviors(DependencyObject uie) - { - return (StylizedBehaviorCollection)uie.GetValue(BehaviorsProperty); - } - - /// - /// Sets Behaviors for element - /// - public static void SetBehaviors(DependencyObject uie, StylizedBehaviorCollection value) - { - uie.SetValue(BehaviorsProperty, value); - } - - private static Behavior GetOriginalBehavior(DependencyObject obj) - { - return obj.GetValue(OriginalBehaviorProperty) as Behavior; - } - - private static int GetIndexOf(BehaviorCollection itemBehaviors, Behavior behavior) - { - int index = -1; - - Behavior orignalBehavior = GetOriginalBehavior(behavior); - - for (int i = 0; i < itemBehaviors.Count; i++) - { - Behavior currentBehavior = itemBehaviors[i]; - - if (currentBehavior == behavior - || currentBehavior == orignalBehavior) - { - index = i; - break; - } - - Behavior currentOrignalBehavior = GetOriginalBehavior(currentBehavior); - - if (currentOrignalBehavior == behavior - || currentOrignalBehavior == orignalBehavior) - { - index = i; - break; - } - } - - return index; - } - - private static void OnPropertyChanged(DependencyObject dpo, DependencyPropertyChangedEventArgs e) - { - var uie = dpo as UIElement; - - if (uie == null) - { - return; - } - - BehaviorCollection itemBehaviors = Interaction.GetBehaviors(uie); - - var newBehaviors = e.NewValue as StylizedBehaviorCollection; - var oldBehaviors = e.OldValue as StylizedBehaviorCollection; - - if (newBehaviors == oldBehaviors) - { - return; - } - - if (oldBehaviors != null) - { - foreach (var behavior in oldBehaviors) - { - int index = GetIndexOf(itemBehaviors, behavior); - - if (index >= 0) - { - itemBehaviors.RemoveAt(index); - } - } - } - - if (newBehaviors != null) - { - foreach (var behavior in newBehaviors) - { - int index = GetIndexOf(itemBehaviors, behavior); - - if (index < 0) - { - var clone = (Behavior)behavior.Clone(); - SetOriginalBehavior(clone, behavior); - itemBehaviors.Add(clone); - } - } - } - } - - private static void SetOriginalBehavior(DependencyObject obj, Behavior value) - { - obj.SetValue(OriginalBehaviorProperty, value); - } - } +using System.Windows; +using System.Windows.Interactivity; + +namespace Fluent.Metro.Behaviours +{ + /// + /// Enables the use of behaviors in styles + /// + public class StylizedBehaviors + { + private static readonly DependencyProperty OriginalBehaviorProperty = DependencyProperty.RegisterAttached(@"OriginalBehaviorInternal", typeof(Behavior), typeof(StylizedBehaviors), new UIPropertyMetadata(null)); + + /// + /// Using a DependencyProperty as the backing store for Behaviors. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty BehaviorsProperty = DependencyProperty.RegisterAttached( + @"Behaviors", + typeof(StylizedBehaviorCollection), + typeof(StylizedBehaviors), + new FrameworkPropertyMetadata(null, OnPropertyChanged)); + + /// + /// Gets Behaviors for element + /// + public static StylizedBehaviorCollection GetBehaviors(DependencyObject uie) + { + return (StylizedBehaviorCollection)uie.GetValue(BehaviorsProperty); + } + + /// + /// Sets Behaviors for element + /// + public static void SetBehaviors(DependencyObject uie, StylizedBehaviorCollection value) + { + uie.SetValue(BehaviorsProperty, value); + } + + private static Behavior GetOriginalBehavior(DependencyObject obj) + { + return obj.GetValue(OriginalBehaviorProperty) as Behavior; + } + + private static int GetIndexOf(BehaviorCollection itemBehaviors, Behavior behavior) + { + int index = -1; + + Behavior orignalBehavior = GetOriginalBehavior(behavior); + + for (int i = 0; i < itemBehaviors.Count; i++) + { + Behavior currentBehavior = itemBehaviors[i]; + + if (currentBehavior == behavior + || currentBehavior == orignalBehavior) + { + index = i; + break; + } + + Behavior currentOrignalBehavior = GetOriginalBehavior(currentBehavior); + + if (currentOrignalBehavior == behavior + || currentOrignalBehavior == orignalBehavior) + { + index = i; + break; + } + } + + return index; + } + + private static void OnPropertyChanged(DependencyObject dpo, DependencyPropertyChangedEventArgs e) + { + var uie = dpo as UIElement; + + if (uie == null) + { + return; + } + + BehaviorCollection itemBehaviors = Interaction.GetBehaviors(uie); + + var newBehaviors = e.NewValue as StylizedBehaviorCollection; + var oldBehaviors = e.OldValue as StylizedBehaviorCollection; + + if (newBehaviors == oldBehaviors) + { + return; + } + + if (oldBehaviors != null) + { + foreach (var behavior in oldBehaviors) + { + int index = GetIndexOf(itemBehaviors, behavior); + + if (index >= 0) + { + itemBehaviors.RemoveAt(index); + } + } + } + + if (newBehaviors != null) + { + foreach (var behavior in newBehaviors) + { + int index = GetIndexOf(itemBehaviors, behavior); + + if (index < 0) + { + var clone = (Behavior)behavior.Clone(); + SetOriginalBehavior(clone, behavior); + itemBehaviors.Add(clone); + } + } + } + } + + private static void SetOriginalBehavior(DependencyObject obj, Behavior value) + { + obj.SetValue(OriginalBehaviorProperty, value); + } + } } \ No newline at end of file diff --git a/Fluent/Metro/Behaviours/StylizedBehaviorsCollection.cs b/Fluent.Ribbon/Metro/Behaviours/StylizedBehaviorsCollection.cs similarity index 96% rename from Fluent/Metro/Behaviours/StylizedBehaviorsCollection.cs rename to Fluent.Ribbon/Metro/Behaviours/StylizedBehaviorsCollection.cs index 7a5fce9a7..65dffba6c 100644 --- a/Fluent/Metro/Behaviours/StylizedBehaviorsCollection.cs +++ b/Fluent.Ribbon/Metro/Behaviours/StylizedBehaviorsCollection.cs @@ -1,22 +1,22 @@ -namespace Fluent.Metro.Behaviours -{ - using System.Windows; - using System.Windows.Interactivity; - - /// - /// Just a for - /// - public class StylizedBehaviorCollection : FreezableCollection - { - /// - /// Creates a new instance of the . - /// - /// - /// The new instance. - /// - protected override Freezable CreateInstanceCore() - { - return new StylizedBehaviorCollection(); - } - } +namespace Fluent.Metro.Behaviours +{ + using System.Windows; + using System.Windows.Interactivity; + + /// + /// Just a for + /// + public class StylizedBehaviorCollection : FreezableCollection + { + /// + /// Creates a new instance of the . + /// + /// + /// The new instance. + /// + protected override Freezable CreateInstanceCore() + { + return new StylizedBehaviorCollection(); + } + } } \ No newline at end of file diff --git a/Fluent/Metro/Controls/WindowCommands.cs b/Fluent.Ribbon/Metro/Controls/WindowCommands.cs similarity index 97% rename from Fluent/Metro/Controls/WindowCommands.cs rename to Fluent.Ribbon/Metro/Controls/WindowCommands.cs index f91352be5..413aad333 100644 --- a/Fluent/Metro/Controls/WindowCommands.cs +++ b/Fluent.Ribbon/Metro/Controls/WindowCommands.cs @@ -1,276 +1,276 @@ -namespace Fluent -{ - using System; - using System.Text; - using System.Windows; - using System.Windows.Controls; - using System.Windows.Media; - using Fluent.Metro.Native; - - /// - /// Contains commands for - /// - [TemplatePart(Name = "PART_Max", Type = typeof(Button))] - [TemplatePart(Name = "PART_Close", Type = typeof(Button))] - [TemplatePart(Name = "PART_Min", Type = typeof(Button))] - public class WindowCommands : ItemsControl, IDisposable - { - private static string minimize; - private static string maximize; - private static string closeText; - private static string restore; - private System.Windows.Controls.Button min; - private System.Windows.Controls.Button max; - private System.Windows.Controls.Button close; - private IntPtr user32 = IntPtr.Zero; - private bool disposed; - - static WindowCommands() - { - DefaultStyleKeyProperty.OverrideMetadata(typeof(WindowCommands), new FrameworkPropertyMetadata(typeof(WindowCommands))); - } - - /// - /// Finalizer - /// - ~WindowCommands() - { - this.Dispose(false); - } - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - public void Dispose() - { - this.Dispose(true); - - GC.SuppressFinalize(this); - } - - /// - /// Dispose(bool disposing) executes in two distinct scenarios. - /// If disposing equals true, the method has been called directly - /// or indirectly by a user's code. Managed and unmanaged resources - /// can be disposed. - /// If disposing equals false, the method has been called by the - /// runtime from inside the finalizer and you should not reference - /// other objects. Only unmanaged resources can be disposed. - /// - protected virtual void Dispose(bool disposing) - { - // Check to see if Dispose has already been called. - if (this.disposed) - { - return; - } - - // If disposing equals true, dispose all managed - // and unmanaged resources. - if (disposing) - { - // Dispose managed resources. - } - - // Call the appropriate methods to clean up - // unmanaged resources here. - // If disposing is false, - // only the following code is executed. - if (this.user32 != IntPtr.Zero) - { - UnsafeNativeMethods.FreeLibrary(this.user32); - this.user32 = IntPtr.Zero; - } - - // Note disposing has been done. - this.disposed = true; - } - - /// - /// Retrieves the translated string for Minimize - /// - public string Minimize - { - get - { - if (string.IsNullOrEmpty(minimize)) - minimize = this.GetCaption(900); - return minimize; - } - } - - /// - /// Retrieves the translated string for Maximize - /// - public string Maximize - { - get - { - if (string.IsNullOrEmpty(maximize)) - maximize = this.GetCaption(901); - return maximize; - } - } - - /// - /// Retrieves the translated string for Close - /// - public string Close - { - get - { - if (string.IsNullOrEmpty(closeText)) - closeText = this.GetCaption(905); - return closeText; - } - } - - /// - /// Retrieves the translated string for Restore - /// - public string Restore - { - get - { - if (string.IsNullOrEmpty(restore)) - { - restore = this.GetCaption(903); - } - - return restore; - } - } - - /// - /// Gets or sets the button brush - /// - public Brush ButtonBrush - { - get { return (Brush)this.GetValue(ButtonBrushProperty); } - set { this.SetValue(ButtonBrushProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for ButtonBrush. This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty ButtonBrushProperty = DependencyProperty.Register("ButtonBrush", typeof(Brush), typeof(WindowCommands), new PropertyMetadata(Brushes.Black)); - - private string GetCaption(int id) - { - if (this.user32 == IntPtr.Zero) - { - this.user32 = UnsafeNativeMethods.LoadLibrary(Environment.SystemDirectory + "\\User32.dll"); - } - - var sb = new StringBuilder(256); - UnsafeNativeMethods.LoadString(this.user32, (uint)id, sb, sb.Capacity); - return sb.ToString().Replace("&", ""); - } - - /// - /// When overridden in a derived class, is invoked whenever application code or internal processes call . - /// - public override void OnApplyTemplate() - { - base.OnApplyTemplate(); - this.close = this.GetTemplateChild("PART_Close") as System.Windows.Controls.Button;//Template.FindName("PART_Close", this) as Button; - if (this.close != null) - this.close.Click += this.CloseClick; - - this.max = this.Template.FindName("PART_Max", this) as System.Windows.Controls.Button; - if (this.max != null) - this.max.Click += this.MaximiseClick; - - this.min = this.Template.FindName("PART_Min", this) as System.Windows.Controls.Button; - if (this.min != null) - this.min.Click += this.MinimiseClick; - - this.RefreshMaximizeIconState(); - } - - private void MinimiseClick(object sender, RoutedEventArgs e) - { - var parentWindow = this.GetParentWindow(); - if (parentWindow != null) - parentWindow.WindowState = WindowState.Minimized; - } - - private void MaximiseClick(object sender, RoutedEventArgs e) - { - var parentWindow = this.GetParentWindow(); - if (parentWindow == null) - return; - - parentWindow.WindowState = parentWindow.WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized; - this.RefreshMaximizeIconState(parentWindow); - } - - /// - /// Upates the visual state of the maximize icon - /// - public void RefreshMaximizeIconState() - { - this.RefreshMaximizeIconState(this.GetParentWindow()); - } - - private void RefreshMaximizeIconState(Window parentWindow) - { - if (parentWindow == null) - return; - - if (parentWindow.WindowState == WindowState.Normal) - { - var maxpath = (UIElement)this.max.FindName("PART_MaximizeButtonContent"); - if (maxpath != null) - { - maxpath.Visibility = Visibility.Visible; - } - - var restorepath = (UIElement)this.max.FindName("PART_RestoreButtonContent"); - if (restorepath != null) - { - restorepath.Visibility = Visibility.Collapsed; - } - - this.max.ToolTip = this.Maximize; - } - else - { - var restorepath = (UIElement)this.max.FindName("PART_RestoreButtonContent"); - if (restorepath != null) - { - restorepath.Visibility = Visibility.Visible; - } - - var maxpath = (UIElement)this.max.FindName("PART_MaximizeButtonContent"); - if (maxpath != null) - { - maxpath.Visibility = Visibility.Collapsed; - } - this.max.ToolTip = this.Restore; - } - } - - private void CloseClick(object sender, RoutedEventArgs e) - { - var parentWindow = this.GetParentWindow(); - if (parentWindow != null) - { - parentWindow.Close(); - } - } - - private Window GetParentWindow() - { - var parent = VisualTreeHelper.GetParent(this); - - while (parent != null && !(parent is Window)) - { - parent = VisualTreeHelper.GetParent(parent); - } - - var parentWindow = parent as Window; - return parentWindow; - } - } +namespace Fluent +{ + using System; + using System.Text; + using System.Windows; + using System.Windows.Controls; + using System.Windows.Media; + using Fluent.Metro.Native; + + /// + /// Contains commands for + /// + [TemplatePart(Name = "PART_Max", Type = typeof(Button))] + [TemplatePart(Name = "PART_Close", Type = typeof(Button))] + [TemplatePart(Name = "PART_Min", Type = typeof(Button))] + public class WindowCommands : ItemsControl, IDisposable + { + private static string minimize; + private static string maximize; + private static string closeText; + private static string restore; + private System.Windows.Controls.Button min; + private System.Windows.Controls.Button max; + private System.Windows.Controls.Button close; + private IntPtr user32 = IntPtr.Zero; + private bool disposed; + + static WindowCommands() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(WindowCommands), new FrameworkPropertyMetadata(typeof(WindowCommands))); + } + + /// + /// Finalizer + /// + ~WindowCommands() + { + this.Dispose(false); + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + this.Dispose(true); + + GC.SuppressFinalize(this); + } + + /// + /// Dispose(bool disposing) executes in two distinct scenarios. + /// If disposing equals true, the method has been called directly + /// or indirectly by a user's code. Managed and unmanaged resources + /// can be disposed. + /// If disposing equals false, the method has been called by the + /// runtime from inside the finalizer and you should not reference + /// other objects. Only unmanaged resources can be disposed. + /// + protected virtual void Dispose(bool disposing) + { + // Check to see if Dispose has already been called. + if (this.disposed) + { + return; + } + + // If disposing equals true, dispose all managed + // and unmanaged resources. + if (disposing) + { + // Dispose managed resources. + } + + // Call the appropriate methods to clean up + // unmanaged resources here. + // If disposing is false, + // only the following code is executed. + if (this.user32 != IntPtr.Zero) + { + UnsafeNativeMethods.FreeLibrary(this.user32); + this.user32 = IntPtr.Zero; + } + + // Note disposing has been done. + this.disposed = true; + } + + /// + /// Retrieves the translated string for Minimize + /// + public string Minimize + { + get + { + if (string.IsNullOrEmpty(minimize)) + minimize = this.GetCaption(900); + return minimize; + } + } + + /// + /// Retrieves the translated string for Maximize + /// + public string Maximize + { + get + { + if (string.IsNullOrEmpty(maximize)) + maximize = this.GetCaption(901); + return maximize; + } + } + + /// + /// Retrieves the translated string for Close + /// + public string Close + { + get + { + if (string.IsNullOrEmpty(closeText)) + closeText = this.GetCaption(905); + return closeText; + } + } + + /// + /// Retrieves the translated string for Restore + /// + public string Restore + { + get + { + if (string.IsNullOrEmpty(restore)) + { + restore = this.GetCaption(903); + } + + return restore; + } + } + + /// + /// Gets or sets the button brush + /// + public Brush ButtonBrush + { + get { return (Brush)this.GetValue(ButtonBrushProperty); } + set { this.SetValue(ButtonBrushProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for ButtonBrush. This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty ButtonBrushProperty = DependencyProperty.Register("ButtonBrush", typeof(Brush), typeof(WindowCommands), new PropertyMetadata(Brushes.Black)); + + private string GetCaption(int id) + { + if (this.user32 == IntPtr.Zero) + { + this.user32 = UnsafeNativeMethods.LoadLibrary(Environment.SystemDirectory + "\\User32.dll"); + } + + var sb = new StringBuilder(256); + UnsafeNativeMethods.LoadString(this.user32, (uint)id, sb, sb.Capacity); + return sb.ToString().Replace("&", ""); + } + + /// + /// When overridden in a derived class, is invoked whenever application code or internal processes call . + /// + public override void OnApplyTemplate() + { + base.OnApplyTemplate(); + this.close = this.GetTemplateChild("PART_Close") as System.Windows.Controls.Button;//Template.FindName("PART_Close", this) as Button; + if (this.close != null) + this.close.Click += this.CloseClick; + + this.max = this.Template.FindName("PART_Max", this) as System.Windows.Controls.Button; + if (this.max != null) + this.max.Click += this.MaximiseClick; + + this.min = this.Template.FindName("PART_Min", this) as System.Windows.Controls.Button; + if (this.min != null) + this.min.Click += this.MinimiseClick; + + this.RefreshMaximizeIconState(); + } + + private void MinimiseClick(object sender, RoutedEventArgs e) + { + var parentWindow = this.GetParentWindow(); + if (parentWindow != null) + parentWindow.WindowState = WindowState.Minimized; + } + + private void MaximiseClick(object sender, RoutedEventArgs e) + { + var parentWindow = this.GetParentWindow(); + if (parentWindow == null) + return; + + parentWindow.WindowState = parentWindow.WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized; + this.RefreshMaximizeIconState(parentWindow); + } + + /// + /// Upates the visual state of the maximize icon + /// + public void RefreshMaximizeIconState() + { + this.RefreshMaximizeIconState(this.GetParentWindow()); + } + + private void RefreshMaximizeIconState(Window parentWindow) + { + if (parentWindow == null) + return; + + if (parentWindow.WindowState == WindowState.Normal) + { + var maxpath = (UIElement)this.max.FindName("PART_MaximizeButtonContent"); + if (maxpath != null) + { + maxpath.Visibility = Visibility.Visible; + } + + var restorepath = (UIElement)this.max.FindName("PART_RestoreButtonContent"); + if (restorepath != null) + { + restorepath.Visibility = Visibility.Collapsed; + } + + this.max.ToolTip = this.Maximize; + } + else + { + var restorepath = (UIElement)this.max.FindName("PART_RestoreButtonContent"); + if (restorepath != null) + { + restorepath.Visibility = Visibility.Visible; + } + + var maxpath = (UIElement)this.max.FindName("PART_MaximizeButtonContent"); + if (maxpath != null) + { + maxpath.Visibility = Visibility.Collapsed; + } + this.max.ToolTip = this.Restore; + } + } + + private void CloseClick(object sender, RoutedEventArgs e) + { + var parentWindow = this.GetParentWindow(); + if (parentWindow != null) + { + parentWindow.Close(); + } + } + + private Window GetParentWindow() + { + var parent = VisualTreeHelper.GetParent(this); + + while (parent != null && !(parent is Window)) + { + parent = VisualTreeHelper.GetParent(parent); + } + + var parentWindow = parent as Window; + return parentWindow; + } + } } \ No newline at end of file diff --git a/Fluent/Metro/MetroColors.cs b/Fluent.Ribbon/Metro/MetroColors.cs similarity index 97% rename from Fluent/Metro/MetroColors.cs rename to Fluent.Ribbon/Metro/MetroColors.cs index f10161734..f49c3cad6 100644 --- a/Fluent/Metro/MetroColors.cs +++ b/Fluent.Ribbon/Metro/MetroColors.cs @@ -1,15 +1,15 @@ -namespace Fluent -{ - using System.Windows; - - /// - /// Collection of some for the Metro/Office 2013 theme - /// - public static class MetroColors - { - /// - /// Gets the theme color - /// - public static readonly ComponentResourceKey ThemeColorKey = new ComponentResourceKey(typeof(MetroColors), "ThemeColor"); - } +namespace Fluent +{ + using System.Windows; + + /// + /// Collection of some for the Metro/Office 2013 theme + /// + public static class MetroColors + { + /// + /// Gets the theme color + /// + public static readonly ComponentResourceKey ThemeColorKey = new ComponentResourceKey(typeof(MetroColors), "ThemeColor"); + } } \ No newline at end of file diff --git a/Fluent/Metro/Native/ABEdge.cs b/Fluent.Ribbon/Metro/Native/ABEdge.cs similarity index 94% rename from Fluent/Metro/Native/ABEdge.cs rename to Fluent.Ribbon/Metro/Native/ABEdge.cs index fbf6b1cf6..fdf972d2e 100644 --- a/Fluent/Metro/Native/ABEdge.cs +++ b/Fluent.Ribbon/Metro/Native/ABEdge.cs @@ -1,10 +1,10 @@ -namespace Fluent.Metro.Native -{ - internal enum ABEdge - { - ABE_LEFT = 0, - ABE_TOP = 1, - ABE_RIGHT = 2, - ABE_BOTTOM = 3 - } +namespace Fluent.Metro.Native +{ + internal enum ABEdge + { + ABE_LEFT = 0, + ABE_TOP = 1, + ABE_RIGHT = 2, + ABE_BOTTOM = 3 + } } \ No newline at end of file diff --git a/Fluent/Metro/Native/ABMsg.cs b/Fluent.Ribbon/Metro/Native/ABMsg.cs similarity index 95% rename from Fluent/Metro/Native/ABMsg.cs rename to Fluent.Ribbon/Metro/Native/ABMsg.cs index ade42f1fa..3321e4b67 100644 --- a/Fluent/Metro/Native/ABMsg.cs +++ b/Fluent.Ribbon/Metro/Native/ABMsg.cs @@ -1,17 +1,17 @@ -namespace Fluent.Metro.Native -{ - internal enum ABMsg - { - ABM_NEW = 0, - ABM_REMOVE = 1, - ABM_QUERYPOS = 2, - ABM_SETPOS = 3, - ABM_GETSTATE = 4, - ABM_GETTASKBARPOS = 5, - ABM_ACTIVATE = 6, - ABM_GETAUTOHIDEBAR = 7, - ABM_SETAUTOHIDEBAR = 8, - ABM_WINDOWPOSCHANGED = 9, - ABM_SETSTATE = 10 - } +namespace Fluent.Metro.Native +{ + internal enum ABMsg + { + ABM_NEW = 0, + ABM_REMOVE = 1, + ABM_QUERYPOS = 2, + ABM_SETPOS = 3, + ABM_GETSTATE = 4, + ABM_GETTASKBARPOS = 5, + ABM_ACTIVATE = 6, + ABM_GETAUTOHIDEBAR = 7, + ABM_SETAUTOHIDEBAR = 8, + ABM_WINDOWPOSCHANGED = 9, + ABM_SETSTATE = 10 + } } \ No newline at end of file diff --git a/Fluent/Metro/Native/APPBARDATA.cs b/Fluent.Ribbon/Metro/Native/APPBARDATA.cs similarity index 95% rename from Fluent/Metro/Native/APPBARDATA.cs rename to Fluent.Ribbon/Metro/Native/APPBARDATA.cs index da0bfe4f7..d60b0ddcd 100644 --- a/Fluent/Metro/Native/APPBARDATA.cs +++ b/Fluent.Ribbon/Metro/Native/APPBARDATA.cs @@ -1,16 +1,16 @@ -namespace Fluent.Metro.Native -{ - using System; - using System.Runtime.InteropServices; - - [StructLayout(LayoutKind.Sequential)] - internal struct APPBARDATA - { - public int cbSize; - public IntPtr hWnd; - public int uCallbackMessage; - public int uEdge; - public RECT rc; - public bool lParam; - } +namespace Fluent.Metro.Native +{ + using System; + using System.Runtime.InteropServices; + + [StructLayout(LayoutKind.Sequential)] + internal struct APPBARDATA + { + public int cbSize; + public IntPtr hWnd; + public int uCallbackMessage; + public int uEdge; + public RECT rc; + public bool lParam; + } } \ No newline at end of file diff --git a/Fluent/Metro/Native/CREATESTRUCT.cs b/Fluent.Ribbon/Metro/Native/CREATESTRUCT.cs similarity index 96% rename from Fluent/Metro/Native/CREATESTRUCT.cs rename to Fluent.Ribbon/Metro/Native/CREATESTRUCT.cs index 61d6f71fd..1fbbd5003 100644 --- a/Fluent/Metro/Native/CREATESTRUCT.cs +++ b/Fluent.Ribbon/Metro/Native/CREATESTRUCT.cs @@ -1,23 +1,23 @@ -using System; -using System.Runtime.InteropServices; - -namespace Fluent.Metro.Native -{ -#pragma warning disable 1591 - [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] - internal struct CREATESTRUCT - { - public IntPtr lpCreateParams; - public IntPtr hInstance; - public IntPtr hMenu; - public IntPtr hwndParent; - public int cy; - public int cx; - public int y; - public int x; - public int style; - public string lpszName; - public string lpszClass; - public int dwExStyle; - } +using System; +using System.Runtime.InteropServices; + +namespace Fluent.Metro.Native +{ +#pragma warning disable 1591 + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] + internal struct CREATESTRUCT + { + public IntPtr lpCreateParams; + public IntPtr hInstance; + public IntPtr hMenu; + public IntPtr hwndParent; + public int cy; + public int cx; + public int y; + public int x; + public int style; + public string lpszName; + public string lpszClass; + public int dwExStyle; + } } \ No newline at end of file diff --git a/Fluent/Metro/Native/Constants.cs b/Fluent.Ribbon/Metro/Native/Constants.cs similarity index 97% rename from Fluent/Metro/Native/Constants.cs rename to Fluent.Ribbon/Metro/Native/Constants.cs index 69f90b2f8..7ed010571 100644 --- a/Fluent/Metro/Native/Constants.cs +++ b/Fluent.Ribbon/Metro/Native/Constants.cs @@ -1,46 +1,46 @@ -namespace Fluent.Metro.Native -{ -#pragma warning disable 1591 -#pragma warning disable 3003 - internal static class Constants - { - public const int MONITOR_DEFAULTTONEAREST = 0x00000002; - - public const int WM_NCCALCSIZE = 0x83; - public const int WM_NCPAINT = 0x85; - public const int WM_NCACTIVATE = 0x86; - public const int WM_GETMINMAXINFO = 0x24; - public const int WM_STYLECHANGED = 0x7d; - public const int WM_CREATE = 0x0001; - public const int WM_NCHITTEST = 0x84; - public const int WM_SIZE = 0x0005; - - public const long WS_MAXIMIZE = 0x01000000; - - public const int GCLP_HBRBACKGROUND = -0x0A; - - public const int HTLEFT = 0x0A; - public const int HTRIGHT = 0x0B; - public const int HTTOP = 0x0C; - public const int HTTOPLEFT = 0x0D; - public const int HTTOPRIGHT = 0x0E; - public const int HTBOTTOM = 0x0F; - public const int HTBOTTOMLEFT = 0x10; - public const int HTBOTTOMRIGHT = 0x11; - public const uint TPM_RETURNCMD = 0x0100; - public const uint TPM_LEFTBUTTON = 0x0; - public const int SW_SHOWNORMAL = 1; - public const int SW_SHOWMINIMIZED = 2; - public const uint SYSCOMMAND = 0x0112; - public const int WM_INITMENU = 0x116; - - public const int SC_MAXIMIZE = 0xF030; - public const int SC_SIZE = 0xF000; - public const int SC_MINIMIZE = 0xF020; - public const int SC_RESTORE = 0xF120; - public const int SC_MOVE = 0xF010; - public const int MF_GRAYED = 0x00000001; - public const int MF_BYCOMMAND = 0x00000000; - public const int MF_ENABLED = 0x00000000; - } +namespace Fluent.Metro.Native +{ +#pragma warning disable 1591 +#pragma warning disable 3003 + internal static class Constants + { + public const int MONITOR_DEFAULTTONEAREST = 0x00000002; + + public const int WM_NCCALCSIZE = 0x83; + public const int WM_NCPAINT = 0x85; + public const int WM_NCACTIVATE = 0x86; + public const int WM_GETMINMAXINFO = 0x24; + public const int WM_STYLECHANGED = 0x7d; + public const int WM_CREATE = 0x0001; + public const int WM_NCHITTEST = 0x84; + public const int WM_SIZE = 0x0005; + + public const long WS_MAXIMIZE = 0x01000000; + + public const int GCLP_HBRBACKGROUND = -0x0A; + + public const int HTLEFT = 0x0A; + public const int HTRIGHT = 0x0B; + public const int HTTOP = 0x0C; + public const int HTTOPLEFT = 0x0D; + public const int HTTOPRIGHT = 0x0E; + public const int HTBOTTOM = 0x0F; + public const int HTBOTTOMLEFT = 0x10; + public const int HTBOTTOMRIGHT = 0x11; + public const uint TPM_RETURNCMD = 0x0100; + public const uint TPM_LEFTBUTTON = 0x0; + public const int SW_SHOWNORMAL = 1; + public const int SW_SHOWMINIMIZED = 2; + public const uint SYSCOMMAND = 0x0112; + public const int WM_INITMENU = 0x116; + + public const int SC_MAXIMIZE = 0xF030; + public const int SC_SIZE = 0xF000; + public const int SC_MINIMIZE = 0xF020; + public const int SC_RESTORE = 0xF120; + public const int SC_MOVE = 0xF010; + public const int MF_GRAYED = 0x00000001; + public const int MF_BYCOMMAND = 0x00000000; + public const int MF_ENABLED = 0x00000000; + } } \ No newline at end of file diff --git a/Fluent/Metro/Native/MARGINS.cs b/Fluent.Ribbon/Metro/Native/MARGINS.cs similarity index 95% rename from Fluent/Metro/Native/MARGINS.cs rename to Fluent.Ribbon/Metro/Native/MARGINS.cs index a673712bf..2ed4c88cd 100644 --- a/Fluent/Metro/Native/MARGINS.cs +++ b/Fluent.Ribbon/Metro/Native/MARGINS.cs @@ -1,14 +1,14 @@ -using System.Runtime.InteropServices; - -namespace Fluent.Metro.Native -{ -#pragma warning disable 1591 - [StructLayout(LayoutKind.Sequential)] - internal struct MARGINS - { - public int leftWidth; - public int rightWidth; - public int topHeight; - public int bottomHeight; - } +using System.Runtime.InteropServices; + +namespace Fluent.Metro.Native +{ +#pragma warning disable 1591 + [StructLayout(LayoutKind.Sequential)] + internal struct MARGINS + { + public int leftWidth; + public int rightWidth; + public int topHeight; + public int bottomHeight; + } } \ No newline at end of file diff --git a/Fluent/Metro/Native/MINMAXINFO.cs b/Fluent.Ribbon/Metro/Native/MINMAXINFO.cs similarity index 96% rename from Fluent/Metro/Native/MINMAXINFO.cs rename to Fluent.Ribbon/Metro/Native/MINMAXINFO.cs index 48a525769..d8cb6be90 100644 --- a/Fluent/Metro/Native/MINMAXINFO.cs +++ b/Fluent.Ribbon/Metro/Native/MINMAXINFO.cs @@ -1,20 +1,20 @@ -using System.Runtime.InteropServices; - -namespace Fluent.Metro.Native -{ -#pragma warning disable 1591 - [StructLayout(LayoutKind.Sequential)] - internal struct MINMAXINFO - { - public POINT ptReserved; - public POINT ptMaxSize; - public POINT ptMaxPosition; - public POINT ptMinTrackSize; - public POINT ptMaxTrackSize; - - public override string ToString() - { - return string.Format("MINMAXINFO {{ ptMaxPosition : {0} / ptMaxSize : {1} }}", this.ptMaxPosition, this.ptMaxSize); - } - }; +using System.Runtime.InteropServices; + +namespace Fluent.Metro.Native +{ +#pragma warning disable 1591 + [StructLayout(LayoutKind.Sequential)] + internal struct MINMAXINFO + { + public POINT ptReserved; + public POINT ptMaxSize; + public POINT ptMaxPosition; + public POINT ptMinTrackSize; + public POINT ptMaxTrackSize; + + public override string ToString() + { + return string.Format("MINMAXINFO {{ ptMaxPosition : {0} / ptMaxSize : {1} }}", this.ptMaxPosition, this.ptMaxSize); + } + }; } \ No newline at end of file diff --git a/Fluent/Metro/Native/MONITORINFO.cs b/Fluent.Ribbon/Metro/Native/MONITORINFO.cs similarity index 96% rename from Fluent/Metro/Native/MONITORINFO.cs rename to Fluent.Ribbon/Metro/Native/MONITORINFO.cs index 127be9999..33208eae1 100644 --- a/Fluent/Metro/Native/MONITORINFO.cs +++ b/Fluent.Ribbon/Metro/Native/MONITORINFO.cs @@ -1,21 +1,21 @@ -using System.Runtime.InteropServices; - -namespace Fluent.Metro.Native -{ - #pragma warning disable 1591 - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] - internal class MONITORINFO - { - public int cbSize = Marshal.SizeOf(typeof(MONITORINFO)); - public RECT rcMonitor = new RECT(); - public RECT rcWork = new RECT(); - public int dwFlags = 0; - - public enum MonitorOptions - { - MONITOR_DEFAULTTONULL = 0x00000000, - MONITOR_DEFAULTTOPRIMARY = 0x00000001, - MONITOR_DEFAULTTONEAREST = 0x00000002 - } - } +using System.Runtime.InteropServices; + +namespace Fluent.Metro.Native +{ + #pragma warning disable 1591 + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + internal class MONITORINFO + { + public int cbSize = Marshal.SizeOf(typeof(MONITORINFO)); + public RECT rcMonitor = new RECT(); + public RECT rcWork = new RECT(); + public int dwFlags = 0; + + public enum MonitorOptions + { + MONITOR_DEFAULTTONULL = 0x00000000, + MONITOR_DEFAULTTOPRIMARY = 0x00000001, + MONITOR_DEFAULTTONEAREST = 0x00000002 + } + } } \ No newline at end of file diff --git a/Fluent/Metro/Native/POINT.cs b/Fluent.Ribbon/Metro/Native/POINT.cs similarity index 95% rename from Fluent/Metro/Native/POINT.cs rename to Fluent.Ribbon/Metro/Native/POINT.cs index 1af834c1d..c57128bc6 100644 --- a/Fluent/Metro/Native/POINT.cs +++ b/Fluent.Ribbon/Metro/Native/POINT.cs @@ -1,63 +1,63 @@ -using System; -using System.Runtime.InteropServices; - -namespace Fluent.Metro.Native -{ -#pragma warning disable 1591 - [Serializable] - [StructLayout(LayoutKind.Sequential)] - internal struct POINT - { - private int x; - private int y; - - public POINT(int x, int y) - { - this.x = x; - this.y = y; - } - - public int X - { - get { return this.x; } - set { this.x = value; } - } - - public int Y - { - get { return this.y; } - set { this.y = value; } - } - - public override bool Equals(object obj) - { - if (obj is POINT) - { - var point = (POINT)obj; - - return point.x == this.x && point.y == this.y; - } - - return base.Equals(obj); - } - public override int GetHashCode() - { - return this.x.GetHashCode() ^ this.y.GetHashCode(); - } - - public static bool operator ==(POINT a, POINT b) - { - return a.x == b.x && a.y == b.y; - } - - public static bool operator !=(POINT a, POINT b) - { - return !(a == b); - } - - public override string ToString() - { - return string.Format("POINT {{ x: {0} / y: {1} }}", this.X, this.Y); - } - } +using System; +using System.Runtime.InteropServices; + +namespace Fluent.Metro.Native +{ +#pragma warning disable 1591 + [Serializable] + [StructLayout(LayoutKind.Sequential)] + internal struct POINT + { + private int x; + private int y; + + public POINT(int x, int y) + { + this.x = x; + this.y = y; + } + + public int X + { + get { return this.x; } + set { this.x = value; } + } + + public int Y + { + get { return this.y; } + set { this.y = value; } + } + + public override bool Equals(object obj) + { + if (obj is POINT) + { + var point = (POINT)obj; + + return point.x == this.x && point.y == this.y; + } + + return base.Equals(obj); + } + public override int GetHashCode() + { + return this.x.GetHashCode() ^ this.y.GetHashCode(); + } + + public static bool operator ==(POINT a, POINT b) + { + return a.x == b.x && a.y == b.y; + } + + public static bool operator !=(POINT a, POINT b) + { + return !(a == b); + } + + public override string ToString() + { + return string.Format("POINT {{ x: {0} / y: {1} }}", this.X, this.Y); + } + } } \ No newline at end of file diff --git a/Fluent/Metro/Native/RECT.cs b/Fluent.Ribbon/Metro/Native/RECT.cs similarity index 96% rename from Fluent/Metro/Native/RECT.cs rename to Fluent.Ribbon/Metro/Native/RECT.cs index fb1f1191b..c07b67193 100644 --- a/Fluent/Metro/Native/RECT.cs +++ b/Fluent.Ribbon/Metro/Native/RECT.cs @@ -1,91 +1,91 @@ -using System; -using System.Runtime.InteropServices; -using System.Windows; - -namespace Fluent.Metro.Native -{ -#pragma warning disable 1591 - [Serializable] - [StructLayout(LayoutKind.Sequential, Pack = 0)] - internal struct RECT - { - public int left; - public int top; - public int right; - public int bottom; - - public static readonly RECT Empty; - - public int Width - { - get { return Math.Abs(this.right - this.left); } // Abs needed for BIDI OS - } - - public int Height - { - get { return this.bottom - this.top; } - } - - public RECT(int left, int top, int right, int bottom) - { - this.left = left; - this.top = top; - this.right = right; - this.bottom = bottom; - } - - public RECT(RECT rcSrc) - { - this.left = rcSrc.left; - this.top = rcSrc.top; - this.right = rcSrc.right; - this.bottom = rcSrc.bottom; - } - - public bool IsEmpty - { - get - { - // BUGBUG : On Bidi OS (hebrew arabic) left > right - return this.left >= this.right || this.top >= this.bottom; - } - } - - public override string ToString() - { - if (this == Empty) - { - return "RECT {Empty}"; - } - - return string.Format("RECT {{ left: {0} / top: {1} / right: {2} / bottom: {3} }}", this.left, this.top, this.right, this.bottom); - } - - /// Determine if 2 RECT are equal (deep compare) - public override bool Equals(object obj) - { - if (!(obj is Rect)) - { - return false; - } - - return (this == (RECT)obj); - } - - /// Return the HashCode for this struct (not garanteed to be unique) - public override int GetHashCode() - { - return this.left.GetHashCode() + this.top.GetHashCode() + this.right.GetHashCode() + this.bottom.GetHashCode(); - } - - public static bool operator ==(RECT rect1, RECT rect2) - { - return (rect1.left == rect2.left && rect1.top == rect2.top && rect1.right == rect2.right && rect1.bottom == rect2.bottom); - } - - public static bool operator !=(RECT rect1, RECT rect2) - { - return !(rect1 == rect2); - } - } +using System; +using System.Runtime.InteropServices; +using System.Windows; + +namespace Fluent.Metro.Native +{ +#pragma warning disable 1591 + [Serializable] + [StructLayout(LayoutKind.Sequential, Pack = 0)] + internal struct RECT + { + public int left; + public int top; + public int right; + public int bottom; + + public static readonly RECT Empty; + + public int Width + { + get { return Math.Abs(this.right - this.left); } // Abs needed for BIDI OS + } + + public int Height + { + get { return this.bottom - this.top; } + } + + public RECT(int left, int top, int right, int bottom) + { + this.left = left; + this.top = top; + this.right = right; + this.bottom = bottom; + } + + public RECT(RECT rcSrc) + { + this.left = rcSrc.left; + this.top = rcSrc.top; + this.right = rcSrc.right; + this.bottom = rcSrc.bottom; + } + + public bool IsEmpty + { + get + { + // BUGBUG : On Bidi OS (hebrew arabic) left > right + return this.left >= this.right || this.top >= this.bottom; + } + } + + public override string ToString() + { + if (this == Empty) + { + return "RECT {Empty}"; + } + + return string.Format("RECT {{ left: {0} / top: {1} / right: {2} / bottom: {3} }}", this.left, this.top, this.right, this.bottom); + } + + /// Determine if 2 RECT are equal (deep compare) + public override bool Equals(object obj) + { + if (!(obj is Rect)) + { + return false; + } + + return (this == (RECT)obj); + } + + /// Return the HashCode for this struct (not garanteed to be unique) + public override int GetHashCode() + { + return this.left.GetHashCode() + this.top.GetHashCode() + this.right.GetHashCode() + this.bottom.GetHashCode(); + } + + public static bool operator ==(RECT rect1, RECT rect2) + { + return (rect1.left == rect2.left && rect1.top == rect2.top && rect1.right == rect2.right && rect1.bottom == rect2.bottom); + } + + public static bool operator !=(RECT rect1, RECT rect2) + { + return !(rect1 == rect2); + } + } } \ No newline at end of file diff --git a/Fluent/Metro/Native/UnsafeNativeMethods.cs b/Fluent.Ribbon/Metro/Native/UnsafeNativeMethods.cs similarity index 98% rename from Fluent/Metro/Native/UnsafeNativeMethods.cs rename to Fluent.Ribbon/Metro/Native/UnsafeNativeMethods.cs index a45e73157..d3183c2c8 100644 --- a/Fluent/Metro/Native/UnsafeNativeMethods.cs +++ b/Fluent.Ribbon/Metro/Native/UnsafeNativeMethods.cs @@ -1,215 +1,215 @@ -using System; -using System.ComponentModel; -using System.Runtime.InteropServices; -using System.Security; -using System.Text; - -namespace Fluent.Metro.Native -{ - using System.Diagnostics.CodeAnalysis; - using System.Runtime.Versioning; - using System.Windows; - - /// http://msdn.microsoft.com/en-us/library/ms182161.aspx - [SuppressUnmanagedCodeSecurity] - internal static class UnsafeNativeMethods - { - [DllImport("user32.dll", SetLastError = true)] - internal static extern bool MoveWindow(IntPtr hwnd, int x, int y, int width, int height, bool repaint); - - /// http://msdn.microsoft.com/en-us/library/windows/desktop/aa969518%28v=vs.85%29.aspx - [DllImport("dwmapi", PreserveSig = false, CallingConvention = CallingConvention.Winapi)] - [return: MarshalAs(UnmanagedType.Bool)] - internal static extern bool DwmIsCompositionEnabled(); - - /// http://msdn.microsoft.com/en-us/library/windows/desktop/aa969512%28v=vs.85%29.aspx - [DllImport("dwmapi", PreserveSig = true, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)] - [return: MarshalAs(UnmanagedType.Error)] - internal static extern int DwmExtendFrameIntoClientArea(IntPtr hWnd, [In] ref MARGINS pMarInset); - - /// http://msdn.microsoft.com/en-us/library/windows/desktop/aa969524%28v=vs.85%29.aspx - [DllImport("dwmapi", PreserveSig = true, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)] - internal static extern int DwmSetWindowAttribute([In] IntPtr hwnd, [In] int attr, [In] ref int attrValue, [In] int attrSize); - - /// http://msdn.microsoft.com/en-us/library/windows/desktop/ms633572%28v=vs.85%29.aspx - [DllImport("user32", CallingConvention = CallingConvention.Winapi)] - internal static extern IntPtr DefWindowProc([In] IntPtr hwnd, [In] int msg, [In] IntPtr wParam, [In] IntPtr lParam); - - /// http://msdn.microsoft.com/en-us/library/dd144901%28v=VS.85%29.aspx - [DllImport("user32", EntryPoint = "GetMonitorInfoW", ExactSpelling = true, CharSet = CharSet.Unicode)] - [return: MarshalAs(UnmanagedType.Bool)] - internal static extern bool GetMonitorInfo([In] IntPtr hMonitor, [Out] MONITORINFO lpmi); - - /// http://msdn.microsoft.com/en-us/library/dd145064%28v=VS.85%29.aspx - [DllImport("user32")] - internal static extern IntPtr MonitorFromWindow([In] IntPtr handle, [In] int flags); - - [DllImport("user32.dll", SetLastError = true)] - internal static extern IntPtr MonitorFromPoint(POINT pt, MONITORINFO.MonitorOptions dwFlags); - - /// http://msdn.microsoft.com/en-us/library/windows/desktop/ms633545(v=vs.85).aspx - [DllImport("user32.dll")] - [return: MarshalAs(UnmanagedType.Bool)] - internal static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags); - - /// http://msdn.microsoft.com/en-us/library/windows/desktop/ms647486%28v=vs.85%29.aspx - [DllImport("user32", CharSet = CharSet.Unicode, ExactSpelling = true, EntryPoint = "LoadStringW", SetLastError = true, CallingConvention = CallingConvention.Winapi)] - internal static extern int LoadString([In] [Optional] IntPtr hInstance, [In] uint uID, [Out] StringBuilder lpBuffer, [In] int nBufferMax); - - /// http://msdn.microsoft.com/en-us/library/windows/desktop/ms633528(v=vs.85).aspx - [DllImport("user32", CharSet = CharSet.Auto, ExactSpelling = true)] - internal static extern bool IsWindow([In] [Optional] IntPtr hWnd); - - /// http://msdn.microsoft.com/en-us/library/windows/desktop/ms647985(v=vs.85).aspx - [DllImport("user32")] - internal static extern IntPtr GetSystemMenu([In] IntPtr hWnd, [In] bool bRevert); - - /// http://msdn.microsoft.com/en-us/library/windows/desktop/ms648003(v=vs.85).aspx - [DllImport("user32")] - internal static extern uint TrackPopupMenuEx([In] IntPtr hmenu, [In] uint fuFlags, [In] int x, [In] int y, [In] IntPtr hwnd, [In] [Optional] IntPtr lptpm); - - /// http://msdn.microsoft.com/en-us/library/windows/desktop/ms644944(v=vs.85).aspx - [DllImport("user32", EntryPoint = "PostMessage", SetLastError = true)] - private static extern bool _PostMessage([In] [Optional] IntPtr hWnd, [In] uint Msg, [In] IntPtr wParam, [In] IntPtr lParam); - - /// http://msdn.microsoft.com/en-us/library/windows/desktop/ms648390(v=vs.85).aspx - [DllImport("user32")] - internal static extern bool GetCursorPos([Out] out POINT pt); - - /// http://msdn.microsoft.com/en-us/library/windows/desktop/ms646258(v=vs.85).aspx - [DllImport("user32", CharSet = CharSet.Auto, ExactSpelling = true)] - internal static extern int GetDoubleClickTime(); - - /// http://msdn.microsoft.com/en-us/library/windows/desktop/ms684175%28v=vs.85%29.aspx - [DllImport("kernel32", CharSet = CharSet.Unicode, ExactSpelling = true, EntryPoint = "LoadLibraryW", SetLastError = true, CallingConvention = CallingConvention.Winapi)] - internal static extern IntPtr LoadLibrary([In] [MarshalAs(UnmanagedType.LPWStr)] string lpFileName); - - /// http://msdn.microsoft.com/en-us/library/windows/desktop/ms683152%28v=vs.85%29.aspx - [DllImport("kernel32", CallingConvention = CallingConvention.Winapi)] - [return: MarshalAs(UnmanagedType.Bool)] - internal static extern bool FreeLibrary([In] IntPtr hModule); - - //SetClassLong won't work correctly for 64-bit: we should use SetClassLongPtr instead. On - //32-bit, SetClassLongPtr is just #defined as SetClassLong. SetClassLong really should - //take/return int instead of IntPtr/HandleRef, but since we're running this only for 32-bit - //it'll be OK. - public static IntPtr SetClassLong(HandleRef hWnd, int nIndex, IntPtr dwNewLong) - { - if (IntPtr.Size == 4) - { - return SetClassLongPtr32(hWnd, nIndex, dwNewLong); - } - return SetClassLongPtr64(hWnd, nIndex, dwNewLong); - } - - [SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable")] - [DllImport("user32.dll", CharSet = CharSet.Auto, EntryPoint = "SetClassLong")] - [ResourceExposure(ResourceScope.None)] - private static extern IntPtr SetClassLongPtr32(HandleRef hwnd, int nIndex, IntPtr dwNewLong); - - [SuppressMessage("Microsoft.Interoperability", "CA1400:PInvokeEntryPointsShouldExist")] - [DllImport("user32.dll", CharSet = CharSet.Auto, EntryPoint = "SetClassLongPtr")] - [ResourceExposure(ResourceScope.None)] - private static extern IntPtr SetClassLongPtr64(HandleRef hwnd, int nIndex, IntPtr dwNewLong); - - internal static IntPtr GetClassLong(IntPtr hWnd, int nIndex) - { - if (IntPtr.Size == 4) - { - return new IntPtr(GetClassLong32(hWnd, nIndex)); - } - - return GetClassLong64(hWnd, nIndex); - } - - [DllImport("user32.dll", EntryPoint = "GetClassLong")] - private static extern uint GetClassLong32(IntPtr hWnd, int nIndex); - - [DllImport("user32.dll", EntryPoint = "GetClassLongPtr")] - [SuppressMessage("Microsoft.Interoperability", "CA1400:PInvokeEntryPointsShouldExist")] - private static extern IntPtr GetClassLong64(IntPtr hWnd, int nIndex); - - [DllImport("gdi32.dll")] - internal static extern IntPtr CreateSolidBrush(int crColor); - - [DllImport("gdi32.dll")] - internal static extern bool DeleteObject(IntPtr hObject); - - [DllImport("user32.dll")] - internal static extern bool SetWindowPlacement(IntPtr hWnd, [In] ref WINDOWPLACEMENT lpwndpl); - - [DllImport("user32.dll")] - internal static extern bool GetWindowPlacement(IntPtr hWnd, out WINDOWPLACEMENT lpwndpl); - - /// http://msdn.microsoft.com/en-us/library/windows/desktop/ms647636(v=vs.85).aspx - [DllImport("user32.dll")] - internal static extern uint EnableMenuItem(IntPtr hMenu, uint itemId, uint uEnable); - - [DllImport("user32.dll")] - public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam); - - [DllImportAttribute("user32.dll")] - public static extern bool ReleaseCapture(); - - internal static void PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam) - { - if (!_PostMessage(hWnd, Msg, wParam, lParam)) - { - throw new Win32Exception(); - } - } - - [StructLayout(LayoutKind.Sequential)] - internal struct Win32Point - { - public readonly int X; - public readonly int Y; - }; - - // See: http://stackoverflow.com/questions/7913325/win-api-in-c-get-hi-and-low-word-from-intptr/7913393#7913393 - internal static Point GetPoint(IntPtr ptr) - { - uint xy = unchecked(IntPtr.Size == 8 ? (uint)ptr.ToInt64() : (uint)ptr.ToInt32()); - int x = unchecked((short)xy); - int y = unchecked((short)(xy >> 16)); - return new Point(x, y); - } - - internal static int GET_X_LPARAM(IntPtr lParam) - { - return LOWORD(lParam.ToInt32()); - } - - internal static int GET_Y_LPARAM(IntPtr lParam) - { - return HIWORD(lParam.ToInt32()); - } - - private static int HIWORD(long i) - { - return (short)(i >> 16); - } - - private static int LOWORD(long i) - { - return (short)(i & 0xFFFF); - } - - internal const int GWL_STYLE = -16; - internal const int WS_SYSMENU = 0x80000; - [DllImport("user32.dll", SetLastError = true)] - internal static extern int GetWindowLong(IntPtr hWnd, int nIndex); - [DllImport("user32.dll")] - internal static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); - - [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)] - internal static extern IntPtr FindWindow(string lpClassName, string lpWindowName); - - [DllImport("shell32.dll", CallingConvention = CallingConvention.StdCall)] - public static extern IntPtr SHAppBarMessage(int dwMessage, ref APPBARDATA pData); - - [DllImport("user32.dll")] - [return: MarshalAs(UnmanagedType.Bool)] - internal static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect); - } +using System; +using System.ComponentModel; +using System.Runtime.InteropServices; +using System.Security; +using System.Text; + +namespace Fluent.Metro.Native +{ + using System.Diagnostics.CodeAnalysis; + using System.Runtime.Versioning; + using System.Windows; + + /// http://msdn.microsoft.com/en-us/library/ms182161.aspx + [SuppressUnmanagedCodeSecurity] + internal static class UnsafeNativeMethods + { + [DllImport("user32.dll", SetLastError = true)] + internal static extern bool MoveWindow(IntPtr hwnd, int x, int y, int width, int height, bool repaint); + + /// http://msdn.microsoft.com/en-us/library/windows/desktop/aa969518%28v=vs.85%29.aspx + [DllImport("dwmapi", PreserveSig = false, CallingConvention = CallingConvention.Winapi)] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool DwmIsCompositionEnabled(); + + /// http://msdn.microsoft.com/en-us/library/windows/desktop/aa969512%28v=vs.85%29.aspx + [DllImport("dwmapi", PreserveSig = true, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)] + [return: MarshalAs(UnmanagedType.Error)] + internal static extern int DwmExtendFrameIntoClientArea(IntPtr hWnd, [In] ref MARGINS pMarInset); + + /// http://msdn.microsoft.com/en-us/library/windows/desktop/aa969524%28v=vs.85%29.aspx + [DllImport("dwmapi", PreserveSig = true, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)] + internal static extern int DwmSetWindowAttribute([In] IntPtr hwnd, [In] int attr, [In] ref int attrValue, [In] int attrSize); + + /// http://msdn.microsoft.com/en-us/library/windows/desktop/ms633572%28v=vs.85%29.aspx + [DllImport("user32", CallingConvention = CallingConvention.Winapi)] + internal static extern IntPtr DefWindowProc([In] IntPtr hwnd, [In] int msg, [In] IntPtr wParam, [In] IntPtr lParam); + + /// http://msdn.microsoft.com/en-us/library/dd144901%28v=VS.85%29.aspx + [DllImport("user32", EntryPoint = "GetMonitorInfoW", ExactSpelling = true, CharSet = CharSet.Unicode)] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool GetMonitorInfo([In] IntPtr hMonitor, [Out] MONITORINFO lpmi); + + /// http://msdn.microsoft.com/en-us/library/dd145064%28v=VS.85%29.aspx + [DllImport("user32")] + internal static extern IntPtr MonitorFromWindow([In] IntPtr handle, [In] int flags); + + [DllImport("user32.dll", SetLastError = true)] + internal static extern IntPtr MonitorFromPoint(POINT pt, MONITORINFO.MonitorOptions dwFlags); + + /// http://msdn.microsoft.com/en-us/library/windows/desktop/ms633545(v=vs.85).aspx + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags); + + /// http://msdn.microsoft.com/en-us/library/windows/desktop/ms647486%28v=vs.85%29.aspx + [DllImport("user32", CharSet = CharSet.Unicode, ExactSpelling = true, EntryPoint = "LoadStringW", SetLastError = true, CallingConvention = CallingConvention.Winapi)] + internal static extern int LoadString([In] [Optional] IntPtr hInstance, [In] uint uID, [Out] StringBuilder lpBuffer, [In] int nBufferMax); + + /// http://msdn.microsoft.com/en-us/library/windows/desktop/ms633528(v=vs.85).aspx + [DllImport("user32", CharSet = CharSet.Auto, ExactSpelling = true)] + internal static extern bool IsWindow([In] [Optional] IntPtr hWnd); + + /// http://msdn.microsoft.com/en-us/library/windows/desktop/ms647985(v=vs.85).aspx + [DllImport("user32")] + internal static extern IntPtr GetSystemMenu([In] IntPtr hWnd, [In] bool bRevert); + + /// http://msdn.microsoft.com/en-us/library/windows/desktop/ms648003(v=vs.85).aspx + [DllImport("user32")] + internal static extern uint TrackPopupMenuEx([In] IntPtr hmenu, [In] uint fuFlags, [In] int x, [In] int y, [In] IntPtr hwnd, [In] [Optional] IntPtr lptpm); + + /// http://msdn.microsoft.com/en-us/library/windows/desktop/ms644944(v=vs.85).aspx + [DllImport("user32", EntryPoint = "PostMessage", SetLastError = true)] + private static extern bool _PostMessage([In] [Optional] IntPtr hWnd, [In] uint Msg, [In] IntPtr wParam, [In] IntPtr lParam); + + /// http://msdn.microsoft.com/en-us/library/windows/desktop/ms648390(v=vs.85).aspx + [DllImport("user32")] + internal static extern bool GetCursorPos([Out] out POINT pt); + + /// http://msdn.microsoft.com/en-us/library/windows/desktop/ms646258(v=vs.85).aspx + [DllImport("user32", CharSet = CharSet.Auto, ExactSpelling = true)] + internal static extern int GetDoubleClickTime(); + + /// http://msdn.microsoft.com/en-us/library/windows/desktop/ms684175%28v=vs.85%29.aspx + [DllImport("kernel32", CharSet = CharSet.Unicode, ExactSpelling = true, EntryPoint = "LoadLibraryW", SetLastError = true, CallingConvention = CallingConvention.Winapi)] + internal static extern IntPtr LoadLibrary([In] [MarshalAs(UnmanagedType.LPWStr)] string lpFileName); + + /// http://msdn.microsoft.com/en-us/library/windows/desktop/ms683152%28v=vs.85%29.aspx + [DllImport("kernel32", CallingConvention = CallingConvention.Winapi)] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool FreeLibrary([In] IntPtr hModule); + + //SetClassLong won't work correctly for 64-bit: we should use SetClassLongPtr instead. On + //32-bit, SetClassLongPtr is just #defined as SetClassLong. SetClassLong really should + //take/return int instead of IntPtr/HandleRef, but since we're running this only for 32-bit + //it'll be OK. + public static IntPtr SetClassLong(HandleRef hWnd, int nIndex, IntPtr dwNewLong) + { + if (IntPtr.Size == 4) + { + return SetClassLongPtr32(hWnd, nIndex, dwNewLong); + } + return SetClassLongPtr64(hWnd, nIndex, dwNewLong); + } + + [SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable")] + [DllImport("user32.dll", CharSet = CharSet.Auto, EntryPoint = "SetClassLong")] + [ResourceExposure(ResourceScope.None)] + private static extern IntPtr SetClassLongPtr32(HandleRef hwnd, int nIndex, IntPtr dwNewLong); + + [SuppressMessage("Microsoft.Interoperability", "CA1400:PInvokeEntryPointsShouldExist")] + [DllImport("user32.dll", CharSet = CharSet.Auto, EntryPoint = "SetClassLongPtr")] + [ResourceExposure(ResourceScope.None)] + private static extern IntPtr SetClassLongPtr64(HandleRef hwnd, int nIndex, IntPtr dwNewLong); + + internal static IntPtr GetClassLong(IntPtr hWnd, int nIndex) + { + if (IntPtr.Size == 4) + { + return new IntPtr(GetClassLong32(hWnd, nIndex)); + } + + return GetClassLong64(hWnd, nIndex); + } + + [DllImport("user32.dll", EntryPoint = "GetClassLong")] + private static extern uint GetClassLong32(IntPtr hWnd, int nIndex); + + [DllImport("user32.dll", EntryPoint = "GetClassLongPtr")] + [SuppressMessage("Microsoft.Interoperability", "CA1400:PInvokeEntryPointsShouldExist")] + private static extern IntPtr GetClassLong64(IntPtr hWnd, int nIndex); + + [DllImport("gdi32.dll")] + internal static extern IntPtr CreateSolidBrush(int crColor); + + [DllImport("gdi32.dll")] + internal static extern bool DeleteObject(IntPtr hObject); + + [DllImport("user32.dll")] + internal static extern bool SetWindowPlacement(IntPtr hWnd, [In] ref WINDOWPLACEMENT lpwndpl); + + [DllImport("user32.dll")] + internal static extern bool GetWindowPlacement(IntPtr hWnd, out WINDOWPLACEMENT lpwndpl); + + /// http://msdn.microsoft.com/en-us/library/windows/desktop/ms647636(v=vs.85).aspx + [DllImport("user32.dll")] + internal static extern uint EnableMenuItem(IntPtr hMenu, uint itemId, uint uEnable); + + [DllImport("user32.dll")] + public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam); + + [DllImportAttribute("user32.dll")] + public static extern bool ReleaseCapture(); + + internal static void PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam) + { + if (!_PostMessage(hWnd, Msg, wParam, lParam)) + { + throw new Win32Exception(); + } + } + + [StructLayout(LayoutKind.Sequential)] + internal struct Win32Point + { + public readonly int X; + public readonly int Y; + }; + + // See: http://stackoverflow.com/questions/7913325/win-api-in-c-get-hi-and-low-word-from-intptr/7913393#7913393 + internal static Point GetPoint(IntPtr ptr) + { + uint xy = unchecked(IntPtr.Size == 8 ? (uint)ptr.ToInt64() : (uint)ptr.ToInt32()); + int x = unchecked((short)xy); + int y = unchecked((short)(xy >> 16)); + return new Point(x, y); + } + + internal static int GET_X_LPARAM(IntPtr lParam) + { + return LOWORD(lParam.ToInt32()); + } + + internal static int GET_Y_LPARAM(IntPtr lParam) + { + return HIWORD(lParam.ToInt32()); + } + + private static int HIWORD(long i) + { + return (short)(i >> 16); + } + + private static int LOWORD(long i) + { + return (short)(i & 0xFFFF); + } + + internal const int GWL_STYLE = -16; + internal const int WS_SYSMENU = 0x80000; + [DllImport("user32.dll", SetLastError = true)] + internal static extern int GetWindowLong(IntPtr hWnd, int nIndex); + [DllImport("user32.dll")] + internal static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + internal static extern IntPtr FindWindow(string lpClassName, string lpWindowName); + + [DllImport("shell32.dll", CallingConvention = CallingConvention.StdCall)] + public static extern IntPtr SHAppBarMessage(int dwMessage, ref APPBARDATA pData); + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect); + } } \ No newline at end of file diff --git a/Fluent/Metro/Native/WINDOWPLACEMENT.cs b/Fluent.Ribbon/Metro/Native/WINDOWPLACEMENT.cs similarity index 96% rename from Fluent/Metro/Native/WINDOWPLACEMENT.cs rename to Fluent.Ribbon/Metro/Native/WINDOWPLACEMENT.cs index 53b232e39..419638ccd 100644 --- a/Fluent/Metro/Native/WINDOWPLACEMENT.cs +++ b/Fluent.Ribbon/Metro/Native/WINDOWPLACEMENT.cs @@ -1,30 +1,30 @@ -using System; -using System.Runtime.InteropServices; - -namespace Fluent.Metro.Native -{ -#pragma warning disable 1591 - [Serializable] - [StructLayout(LayoutKind.Sequential)] - internal struct WINDOWPLACEMENT - { - public int length; - - public int flags; - - public int showCmd; - - public POINT minPosition; - - public POINT maxPosition; - - public RECT normalPosition; - - public override string ToString() - { - return string.Format( - "WINDOWPLACEMENT\n{{\nlength: {0}\nflags: {1}\nshowCmd: {2}\nminPosition: {3}\nmaxPosition: {4}\nnormalPosition: {5}\n}}" - , this.length, this.flags, this.showCmd, this.minPosition, this.maxPosition, this.normalPosition); - } - } +using System; +using System.Runtime.InteropServices; + +namespace Fluent.Metro.Native +{ +#pragma warning disable 1591 + [Serializable] + [StructLayout(LayoutKind.Sequential)] + internal struct WINDOWPLACEMENT + { + public int length; + + public int flags; + + public int showCmd; + + public POINT minPosition; + + public POINT maxPosition; + + public RECT normalPosition; + + public override string ToString() + { + return string.Format( + "WINDOWPLACEMENT\n{{\nlength: {0}\nflags: {1}\nshowCmd: {2}\nminPosition: {3}\nmaxPosition: {4}\nnormalPosition: {5}\n}}" + , this.length, this.flags, this.showCmd, this.minPosition, this.maxPosition, this.normalPosition); + } + } } \ No newline at end of file diff --git a/Fluent/Properties/AssemblyInfo.cs b/Fluent.Ribbon/Properties/AssemblyInfo.cs similarity index 97% rename from Fluent/Properties/AssemblyInfo.cs rename to Fluent.Ribbon/Properties/AssemblyInfo.cs index 4a409d18e..2ef2c5db1 100644 --- a/Fluent/Properties/AssemblyInfo.cs +++ b/Fluent.Ribbon/Properties/AssemblyInfo.cs @@ -1,13 +1,13 @@ -using System.Reflection; -using System.Runtime.InteropServices; -using System.Windows.Markup; - -[assembly: AssemblyTitle("Fluent")] -[assembly: AssemblyDescription("Fluent Ribbon Control Suite")] - -[assembly: Guid("d849f751-e8f4-480d-acc0-6148eafcaafc")] - -[assembly: XmlnsPrefix("urn:fluent-ribbon", "fluent")] -[assembly: XmlnsDefinition("urn:fluent-ribbon", "Fluent")] -[assembly: XmlnsDefinition("urn:fluent-ribbon", "Fluent.Converters")] +using System.Reflection; +using System.Runtime.InteropServices; +using System.Windows.Markup; + +[assembly: AssemblyTitle("Fluent")] +[assembly: AssemblyDescription("Fluent Ribbon Control Suite")] + +[assembly: Guid("d849f751-e8f4-480d-acc0-6148eafcaafc")] + +[assembly: XmlnsPrefix("urn:fluent-ribbon", "fluent")] +[assembly: XmlnsDefinition("urn:fluent-ribbon", "Fluent")] +[assembly: XmlnsDefinition("urn:fluent-ribbon", "Fluent.Converters")] [assembly: XmlnsDefinition("urn:fluent-ribbon", "Fluent.Metro.Behaviours")] \ No newline at end of file diff --git a/Fluent/Properties/FluentStrongName.snk b/Fluent.Ribbon/Properties/FluentStrongName.snk similarity index 100% rename from Fluent/Properties/FluentStrongName.snk rename to Fluent.Ribbon/Properties/FluentStrongName.snk diff --git a/Fluent/RibbonCommands.cs b/Fluent.Ribbon/RibbonCommands.cs similarity index 96% rename from Fluent/RibbonCommands.cs rename to Fluent.Ribbon/RibbonCommands.cs index 7cbae8e66..cb5b9232c 100644 --- a/Fluent/RibbonCommands.cs +++ b/Fluent.Ribbon/RibbonCommands.cs @@ -1,20 +1,20 @@ -namespace Fluent -{ - using System.Windows.Input; - - /// - /// Class for several commands belonging to the Ribbon - /// - public static class RibbonCommands - { - private static readonly RoutedUICommand openBackstage = new RoutedUICommand("Open backstage", "OpenBackstage", typeof(RibbonCommands)); - - /// - /// Gets the value that represents the Open Backstage command - /// - public static RoutedCommand OpenBackstage - { - get { return openBackstage; } - } - } +namespace Fluent +{ + using System.Windows.Input; + + /// + /// Class for several commands belonging to the Ribbon + /// + public static class RibbonCommands + { + private static readonly RoutedUICommand openBackstage = new RoutedUICommand("Open backstage", "OpenBackstage", typeof(RibbonCommands)); + + /// + /// Gets the value that represents the Open Backstage command + /// + public static RoutedCommand OpenBackstage + { + get { return openBackstage; } + } + } } \ No newline at end of file diff --git a/Fluent/RibbonControlSizeDefinition.cs b/Fluent.Ribbon/RibbonControlSizeDefinition.cs similarity index 97% rename from Fluent/RibbonControlSizeDefinition.cs rename to Fluent.Ribbon/RibbonControlSizeDefinition.cs index c067920a9..57218704b 100644 --- a/Fluent/RibbonControlSizeDefinition.cs +++ b/Fluent.Ribbon/RibbonControlSizeDefinition.cs @@ -1,141 +1,141 @@ -namespace Fluent -{ - using System; - using System.ComponentModel; - using System.Linq; - using Fluent.Converters; - - /// - /// Class to map from to - /// - [TypeConverter(typeof(SizeDefinitionConverter))] - public struct RibbonControlSizeDefinition - { - private const int MaxSizeDefinitionParts = 3; - - /// - /// Creates a new instance - /// - public RibbonControlSizeDefinition(RibbonControlSize large, RibbonControlSize middle, RibbonControlSize small) - : this() - { - this.Large = large; - this.Middle = middle; - this.Small = small; - } - - /// - /// Creates a new instance - /// - public RibbonControlSizeDefinition(string sizeDefinition) - : this() - { - if (string.IsNullOrEmpty(sizeDefinition)) - { - this.Large = RibbonControlSize.Large; - this.Middle = RibbonControlSize.Large; - this.Small = RibbonControlSize.Large; - return; - } - - var splitted = sizeDefinition.Split(new[] { ' ', ',', ';', '-', '>' }, MaxSizeDefinitionParts, StringSplitOptions.RemoveEmptyEntries).ToList(); - - if (splitted.Count == 0) - { - this.Large = RibbonControlSize.Large; - this.Middle = RibbonControlSize.Large; - this.Small = RibbonControlSize.Large; - return; - } - - // Ensure that we got three sizes - for (var i = splitted.Count; i < MaxSizeDefinitionParts; i++) - { - splitted.Add(splitted[splitted.Count - 1]); - } - - this.Large = ToRibbonControlSize(splitted[0]); - this.Middle = ToRibbonControlSize(splitted[1]); - this.Small = ToRibbonControlSize(splitted[2]); - } - - /// - /// Gets or sets the value for large group sizes - /// - public RibbonControlSize Large { get; set; } - - /// - /// Gets or sets the value for middle group sizes - /// - public RibbonControlSize Middle { get; set; } - - /// - /// Gets or sets the value for small group sizes - /// - public RibbonControlSize Small { get; set; } - - /// - /// Converts from to - /// - public static implicit operator RibbonControlSizeDefinition(string sizeDefinition) - { - return new RibbonControlSizeDefinition(sizeDefinition); - } - - /// - /// Converts from to - /// - public static implicit operator string(RibbonControlSizeDefinition sizeDefinition) - { - return sizeDefinition.ToString(); - } - - /// - /// Converts from to - /// - public static RibbonControlSize ToRibbonControlSize(string ribbonControlSize) - { - RibbonControlSize result; - - return Enum.TryParse(ribbonControlSize, true, out result) - ? result - : RibbonControlSize.Large; - } - - /// - /// Gets the appropriate from , or depending on - /// - public RibbonControlSize GetSize(RibbonGroupBoxState ribbonGroupBoxState) - { - switch (ribbonGroupBoxState) - { - case RibbonGroupBoxState.Large: - return this.Large; - - case RibbonGroupBoxState.Middle: - return this.Middle; - - case RibbonGroupBoxState.Small: - return this.Small; - - case RibbonGroupBoxState.Collapsed: - case RibbonGroupBoxState.QuickAccess: - return this.Large; - - default: - return RibbonControlSize.Large; - } - } - - /// - /// Returns a string that represents the current object. - /// - /// - /// A string that represents the current object. - /// - public override string ToString() - { - return string.Format("{0} {1} {2}", this.Large, this.Middle, this.Small); - } - } +namespace Fluent +{ + using System; + using System.ComponentModel; + using System.Linq; + using Fluent.Converters; + + /// + /// Class to map from to + /// + [TypeConverter(typeof(SizeDefinitionConverter))] + public struct RibbonControlSizeDefinition + { + private const int MaxSizeDefinitionParts = 3; + + /// + /// Creates a new instance + /// + public RibbonControlSizeDefinition(RibbonControlSize large, RibbonControlSize middle, RibbonControlSize small) + : this() + { + this.Large = large; + this.Middle = middle; + this.Small = small; + } + + /// + /// Creates a new instance + /// + public RibbonControlSizeDefinition(string sizeDefinition) + : this() + { + if (string.IsNullOrEmpty(sizeDefinition)) + { + this.Large = RibbonControlSize.Large; + this.Middle = RibbonControlSize.Large; + this.Small = RibbonControlSize.Large; + return; + } + + var splitted = sizeDefinition.Split(new[] { ' ', ',', ';', '-', '>' }, MaxSizeDefinitionParts, StringSplitOptions.RemoveEmptyEntries).ToList(); + + if (splitted.Count == 0) + { + this.Large = RibbonControlSize.Large; + this.Middle = RibbonControlSize.Large; + this.Small = RibbonControlSize.Large; + return; + } + + // Ensure that we got three sizes + for (var i = splitted.Count; i < MaxSizeDefinitionParts; i++) + { + splitted.Add(splitted[splitted.Count - 1]); + } + + this.Large = ToRibbonControlSize(splitted[0]); + this.Middle = ToRibbonControlSize(splitted[1]); + this.Small = ToRibbonControlSize(splitted[2]); + } + + /// + /// Gets or sets the value for large group sizes + /// + public RibbonControlSize Large { get; set; } + + /// + /// Gets or sets the value for middle group sizes + /// + public RibbonControlSize Middle { get; set; } + + /// + /// Gets or sets the value for small group sizes + /// + public RibbonControlSize Small { get; set; } + + /// + /// Converts from to + /// + public static implicit operator RibbonControlSizeDefinition(string sizeDefinition) + { + return new RibbonControlSizeDefinition(sizeDefinition); + } + + /// + /// Converts from to + /// + public static implicit operator string(RibbonControlSizeDefinition sizeDefinition) + { + return sizeDefinition.ToString(); + } + + /// + /// Converts from to + /// + public static RibbonControlSize ToRibbonControlSize(string ribbonControlSize) + { + RibbonControlSize result; + + return Enum.TryParse(ribbonControlSize, true, out result) + ? result + : RibbonControlSize.Large; + } + + /// + /// Gets the appropriate from , or depending on + /// + public RibbonControlSize GetSize(RibbonGroupBoxState ribbonGroupBoxState) + { + switch (ribbonGroupBoxState) + { + case RibbonGroupBoxState.Large: + return this.Large; + + case RibbonGroupBoxState.Middle: + return this.Middle; + + case RibbonGroupBoxState.Small: + return this.Small; + + case RibbonGroupBoxState.Collapsed: + case RibbonGroupBoxState.QuickAccess: + return this.Large; + + default: + return RibbonControlSize.Large; + } + } + + /// + /// Returns a string that represents the current object. + /// + /// + /// A string that represents the current object. + /// + public override string ToString() + { + return string.Format("{0} {1} {2}", this.Large, this.Middle, this.Small); + } + } } \ No newline at end of file diff --git a/Fluent/RibbonGroupBoxPanel.cs b/Fluent.Ribbon/RibbonGroupBoxPanel.cs similarity index 96% rename from Fluent/RibbonGroupBoxPanel.cs rename to Fluent.Ribbon/RibbonGroupBoxPanel.cs index e8f85d6aa..2c4a703e4 100644 --- a/Fluent/RibbonGroupBoxPanel.cs +++ b/Fluent.Ribbon/RibbonGroupBoxPanel.cs @@ -1,19 +1,19 @@ -#region Copyright and License Information -// Fluent Ribbon Control Suite -// http://fluent.codeplex.com/ -// Copyright � Degtyarev Daniel, Rikker Serg. 2009-2010. All rights reserved. -// -// Distributed under the terms of the Microsoft Public License (Ms-PL). -// The license is available online http://fluent.codeplex.com/license -#endregion -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Fluent -{ - /*class RibbonGroupBoxPanel - { - }*/ -} +#region Copyright and License Information +// Fluent Ribbon Control Suite +// http://fluent.codeplex.com/ +// Copyright � Degtyarev Daniel, Rikker Serg. 2009-2010. All rights reserved. +// +// Distributed under the terms of the Microsoft Public License (Ms-PL). +// The license is available online http://fluent.codeplex.com/license +#endregion +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Fluent +{ + /*class RibbonGroupBoxPanel + { + }*/ +} diff --git a/Fluent/RibbonLocalization.cs b/Fluent.Ribbon/RibbonLocalization.cs similarity index 98% rename from Fluent/RibbonLocalization.cs rename to Fluent.Ribbon/RibbonLocalization.cs index 431eb0780..983ad366a 100644 --- a/Fluent/RibbonLocalization.cs +++ b/Fluent.Ribbon/RibbonLocalization.cs @@ -1,2217 +1,2217 @@ -using System.ComponentModel; -using System.Globalization; - -namespace Fluent -{ - /// - /// Contains localizable Ribbon's properties. - /// Set Culture property to change current Ribbon localization or - /// set properties independently to use your localization - /// - public class RibbonLocalization : INotifyPropertyChanged - { - #region Implementation of INotifyPropertyChanged - - /// - /// Occurs then property is changed - /// - public event PropertyChangedEventHandler PropertyChanged; - - // Raises PropertYChanegd event - private void RaisePropertyChanged(string propertyName) - { - if (this.PropertyChanged != null) - this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); - } - - #endregion - - #region Culture - - private CultureInfo culture; - - /// - /// Gets or sets current culture used for localization - /// - public CultureInfo Culture - { - get { return this.culture; } - set - { - if (!Equals(this.culture, value)) - { - this.culture = value; - this.LoadCulture(this.culture); - this.RaisePropertyChanged("Culture"); - } - } - } - - #endregion - - #region Text of backstage button - - // Text of backstage button - private string backstageButtonText; - - /// - /// Gets or sets text of backstage button - /// - public string BackstageButtonText - { - get { return this.backstageButtonText; } - set - { - if (this.backstageButtonText != value) - { - this.backstageButtonText = value; - this.RaisePropertyChanged("BackstageButtonText"); - } - } - } - - #endregion - - #region KeyTip of backstage button - - // KeyTip of backstage button - private string backstageButtonKeyTip; - - /// - /// Gets or sets KeyTip of backstage button - /// - public string BackstageButtonKeyTip - { - get { return this.backstageButtonKeyTip; } - set - { - if (this.backstageButtonKeyTip != value) - { - this.backstageButtonKeyTip = value; - this.RaisePropertyChanged("BackstageButtonKeyTip"); - } - } - } - - #endregion - - #region Minimize Button ScreenTip Title - - // Minimize Button ScreenTip Title - private string minimizeButtonScreenTipTitle; - - /// - /// Minimize Button ScreenTip Title - /// - public string MinimizeButtonScreenTipTitle - { - get { return this.minimizeButtonScreenTipTitle; } - set - { - if (this.minimizeButtonScreenTipTitle != value) - { - this.minimizeButtonScreenTipTitle = value; - this.RaisePropertyChanged("MinimizeButtonScreenTipTitle"); - } - } - } - - #endregion - - #region Minimize Button ScreenTip Text - - // Minimize Button ScreenTip Text - private string minimizeButtonScreenTipText; - - /// - /// Minimize Button ScreenTip Text - /// - public string MinimizeButtonScreenTipText - { - get { return this.minimizeButtonScreenTipText; } - set - { - if (this.minimizeButtonScreenTipText != value) - { - this.minimizeButtonScreenTipText = value; - this.RaisePropertyChanged("MinimizeButtonScreenTipText"); - } - } - } - - #endregion - - #region Expand Button ScreenTip Title - - // Expand Button ScreenTip Title - private string expandButtonScreenTipTitle; - - /// - /// Expand Button ScreenTip Title - /// - public string ExpandButtonScreenTipTitle - { - get { return this.expandButtonScreenTipTitle; } - set - { - if (this.expandButtonScreenTipTitle != value) - { - this.expandButtonScreenTipTitle = value; - this.RaisePropertyChanged("ExpandButtonScreenTipTitle"); - } - } - } - - #endregion - - #region Expand Button ScreenTip Text - - // Expand Button ScreenTip Text - private string expandButtonScreenTipText; - - /// - /// Expand Button ScreenTip Text - /// - public string ExpandButtonScreenTipText - { - get { return this.expandButtonScreenTipText; } - set - { - if (this.expandButtonScreenTipText != value) - { - this.expandButtonScreenTipText = value; - this.RaisePropertyChanged("ExpandButtonScreenTipText"); - } - } - } - - #endregion - - #region Quick Access ToolBar DropDown Button ToolTip - - // Quick Access ToolBar DropDown Button ToolTip - private string quickAccessToolBarDropDownButtonTooltip; - - /// - /// Quick Access ToolBar DropDown Button ToolTip - /// - public string QuickAccessToolBarDropDownButtonTooltip - { - get { return this.quickAccessToolBarDropDownButtonTooltip; } - set - { - if (this.quickAccessToolBarDropDownButtonTooltip != value) - { - this.quickAccessToolBarDropDownButtonTooltip = value; - this.RaisePropertyChanged("QuickAccessToolBarDropDownButtonTooltip"); - } - } - } - - #endregion - - #region Quick Access ToolBar MoreControls Button ToolTip - - // Quick Access ToolBar MoreControls Button ToolTip - private string quickAccessToolBarMoreControlsButtonTooltip; - - /// - /// Quick Access ToolBar MoreControls Button ToolTip - /// - public string QuickAccessToolBarMoreControlsButtonTooltip - { - get { return this.quickAccessToolBarMoreControlsButtonTooltip; } - set - { - if (this.quickAccessToolBarMoreControlsButtonTooltip != value) - { - this.quickAccessToolBarMoreControlsButtonTooltip = value; - this.RaisePropertyChanged("QuickAccessToolBarMoreControlsButtonTooltip"); - } - } - } - - #endregion - - #region Quick Access ToolBar Menu Header - - // Quick Access ToolBar Menu Header - private string quickAccessToolBarMenuHeader; - - /// - /// Quick Access ToolBar Menu Header - /// - public string QuickAccessToolBarMenuHeader - { - get { return this.quickAccessToolBarMenuHeader; } - set - { - if (this.quickAccessToolBarMenuHeader != value) - { - this.quickAccessToolBarMenuHeader = value; - this.RaisePropertyChanged("QuickAccessToolBarMenuHeader"); - } - } - } - - #endregion - - #region Quick Access ToolBar Context Menu Show Below - - // Quick Access ToolBar Minimize Quick Access Toolbar - private string quickAccessToolBarMenuShowBelow; - - /// - /// Quick Access ToolBar Minimize Quick Access Toolbar - /// - public string QuickAccessToolBarMenuShowBelow - { - get { return this.quickAccessToolBarMenuShowBelow; } - set - { - if (this.quickAccessToolBarMenuShowBelow != value) - { - this.quickAccessToolBarMenuShowBelow = value; - this.RaisePropertyChanged("QuickAccessToolBarMenuShowBelow"); - } - } - } - - #endregion - - #region Quick Access ToolBar Menu Show Above - - // Quick Access ToolBar Menu Minimize Quick Access Toolbar - private string quickAccessToolBarMenuShowAbove; - - /// - /// Quick Access ToolBar Menu Minimize Quick Access Toolbar - /// - public string QuickAccessToolBarMenuShowAbove - { - get { return this.quickAccessToolBarMenuShowAbove; } - set - { - if (this.quickAccessToolBarMenuShowAbove != value) - { - this.quickAccessToolBarMenuShowAbove = value; - this.RaisePropertyChanged("QuickAccessToolBarMenuShowAbove"); - } - } - } - - #endregion - - #region Quick Access ToolBar Menu Add Item - - // Quick Access ToolBar Menu Add Item - private string ribbonContextMenuAddItem; - - /// - /// Quick Access ToolBar Menu Add Item - /// - public string RibbonContextMenuAddItem - { - get { return this.ribbonContextMenuAddItem; } - set - { - if (this.ribbonContextMenuAddItem != value) - { - this.ribbonContextMenuAddItem = value; - this.RaisePropertyChanged("RibbonContextMenuAddItem"); - } - } - } - - #endregion - - #region Quick Access ToolBar Menu Add Group - - // Quick Access ToolBar Menu Add Group - private string ribbonContextMenuAddGroup; - - /// - /// Quick Access ToolBar Menu Add Group - /// - public string RibbonContextMenuAddGroup - { - get { return this.ribbonContextMenuAddGroup; } - set - { - if (this.ribbonContextMenuAddGroup != value) - { - this.ribbonContextMenuAddGroup = value; - this.RaisePropertyChanged("RibbonContextMenuAddGroup"); - } - } - } - - #endregion - - #region Quick Access ToolBar Menu Add Gallery - - // Quick Access ToolBar Menu Add Gallery - private string ribbonContextMenuAddGallery; - - /// - /// Quick Access ToolBar Menu Add Gallery - /// - public string RibbonContextMenuAddGallery - { - get { return this.ribbonContextMenuAddGallery; } - set - { - if (this.ribbonContextMenuAddGallery != value) - { - this.ribbonContextMenuAddGallery = value; - this.RaisePropertyChanged("RibbonContextMenuAddGallery"); - } - } - } - - #endregion - - #region Quick Access ToolBar Menu Add Menu - - // Quick Access ToolBar Menu Add Menu - private string ribbonContextMenuAddMenu; - - /// - /// Quick Access ToolBar Menu Add Menu - /// - public string RibbonContextMenuAddMenu - { - get { return this.ribbonContextMenuAddMenu; } - set - { - if (this.ribbonContextMenuAddMenu != value) - { - this.ribbonContextMenuAddMenu = value; - this.RaisePropertyChanged("RibbonContextMenuAddMenu"); - } - } - } - - #endregion - - #region Quick Access ToolBar Menu Remove Item - - // Quick Access ToolBar Menu Remove Item - private string ribbonContextMenuRemoveItem; - - /// - /// Quick Access ToolBar Menu Remove Item - /// - public string RibbonContextMenuRemoveItem - { - get { return this.ribbonContextMenuRemoveItem; } - set - { - if (this.ribbonContextMenuRemoveItem != value) - { - this.ribbonContextMenuRemoveItem = value; - this.RaisePropertyChanged("RibbonContextMenuRemoveItem"); - } - } - } - - #endregion - - #region Ribbon Context Menu Customize Quick Access Toolbar - - // Ribbon Context Menu Customize Quick Access Toolbar - private string ribbonContextMenuCustomizeQuickAccessToolbar; - - /// - /// Ribbon Context Menu Customize Quick Access Toolbar - /// - public string RibbonContextMenuCustomizeQuickAccessToolBar - { - get { return this.ribbonContextMenuCustomizeQuickAccessToolbar; } - set - { - if (this.ribbonContextMenuCustomizeQuickAccessToolbar != value) - { - this.ribbonContextMenuCustomizeQuickAccessToolbar = value; - this.RaisePropertyChanged("RibbonContextMenuCustomizeQuickAccessToolBar"); - } - } - } - - #endregion - - #region Ribbon Context Menu Customize Ribbon - - // Ribbon Context Menu Customize Quick Access Toolbar - private string ribbonContextMenuCustomizeRibbon; - - /// - /// Ribbon Context Menu Customize Quick Access Toolbar - /// - public string RibbonContextMenuCustomizeRibbon - { - get { return this.ribbonContextMenuCustomizeRibbon; } - set - { - if (this.ribbonContextMenuCustomizeRibbon != value) - { - this.ribbonContextMenuCustomizeRibbon = value; - this.RaisePropertyChanged("RibbonContextMenuCustomizeRibbon"); - } - } - } - - #endregion - - #region Ribbon Context Menu Minimize Ribbon - - // Ribbon Context Menu Minimize Quick Access Toolbar - private string ribbonContextMenuMinimizeRibbon; - - /// - /// Ribbon Context Menu Minimize Quick Access Toolbar - /// - public string RibbonContextMenuMinimizeRibbon - { - get { return this.ribbonContextMenuMinimizeRibbon; } - set - { - if (this.ribbonContextMenuMinimizeRibbon != value) - { - this.ribbonContextMenuMinimizeRibbon = value; - this.RaisePropertyChanged("RibbonContextMenuMinimizeRibbon"); - } - } - } - - #endregion - - #region Ribbon Context Menu Show Below - - // Ribbon Context Menu Minimize Quick Access Toolbar - private string ribbonContextMenuShowBelow; - - /// - /// Ribbon Context Menu Minimize Quick Access Toolbar - /// - public string RibbonContextMenuShowBelow - { - get { return this.ribbonContextMenuShowBelow; } - set - { - if (this.ribbonContextMenuShowBelow != value) - { - this.ribbonContextMenuShowBelow = value; - this.RaisePropertyChanged("RibbonContextMenuShowBelow"); - } - } - } - - #endregion - - #region Ribbon Context Menu Show Above - - // Ribbon Context Menu Minimize Quick Access Toolbar - private string ribbonContextMenuShowAbove; - - /// - /// Ribbon Context Menu Minimize Quick Access Toolbar - /// - public string RibbonContextMenuShowAbove - { - get { return this.ribbonContextMenuShowAbove; } - set - { - if (this.ribbonContextMenuShowAbove != value) - { - this.ribbonContextMenuShowAbove = value; - this.RaisePropertyChanged("RibbonContextMenuShowAbove"); - } - } - } - - #endregion - - #region ScreenTipDisableReasonHeader - - // ScreenTip's Disable Reason Header - private string screenTipDisableReasonHeader; - - /// - /// Gets or sets ScreenTip's disable reason header - /// - public string ScreenTipDisableReasonHeader - { - get { return this.screenTipDisableReasonHeader; } - set - { - if (this.screenTipDisableReasonHeader != value) - { - this.screenTipDisableReasonHeader = value; - this.RaisePropertyChanged("ScreenTipDisableReasonHeader"); - } - } - } - - #endregion - - #region ScreenTipF1Label - - // ScreenTip's Disable Reason Header - private string screenTipF1LabelHeader; - - /// - /// Gets or sets ScreenTip's disable reason header - /// - public string ScreenTipF1LabelHeader - { - get { return this.screenTipF1LabelHeader; } - set - { - if (this.screenTipF1LabelHeader != value) - { - this.screenTipF1LabelHeader = value; - this.RaisePropertyChanged("ScreenTipF1LabelHeader"); - } - } - } - - #endregion - - #region Customize Status Bar - - // Text of backstage button - private string customizeStatusBar; - - /// - /// Gets or sets customize Status Bar - /// - public string CustomizeStatusBar - { - get { return this.customizeStatusBar; } - set - { - if (this.customizeStatusBar != value) - { - this.customizeStatusBar = value; - this.RaisePropertyChanged("CustomizeStatusBar"); - } - } - } - - #endregion - - #region Initialization - - /// - /// Default constructor - /// - public RibbonLocalization() - { - // Fallback values - this.LoadEnglish(); - - this.Culture = CultureInfo.CurrentUICulture; - } - - #endregion - - #region Methods - - // Coerce all localized values - private void LoadCulture(CultureInfo culture) - { - var language = culture.TwoLetterISOLanguageName; - - switch (language) - { - case "en": - this.LoadEnglish(); - break; - - case "ru": - this.LoadRussian(); - break; - - case "uk": - this.LoadUkrainian(); - break; - - case "fa": - this.LoadPersian(); - break; - - case "de": - this.LoadGerman(); - break; - - case "hu": - this.LoadHungarian(); - break; - - case "cs": - this.LoadCzech(); - break; - - case "fr": - this.LoadFrench(); - break; - - case "pl": - this.LoadPolish(); - break; - - case "ja": - this.LoadJapanese(); - break; - - case "nl": - this.LoadDutch(); - break; - case "pt": - { - if (culture.Name == "pt-BR") - { - this.LoadPortugueseBrazilian(); - } - else - { - this.LoadPortuguese(); - } - break; - } - - case "es": - this.LoadSpanish(); - break; - - //edit by gamegear.tw - //note: add TradChinese - case "zh": - if(culture.Name=="zh-tw") - { - this.LoadTradChinese(); - }else - { - this.LoadChinese(); - } - - break; - - case "sv": - this.LoadSwedish(); - break; - - case "sk": - this.LoadSlovak(); - break; - - case "ro": - this.LoadRomanian(); - break; - - case "it": - this.LoadItalian(); - break; - - case "ar": - this.LoadArabic(); - break; - - case "da": - this.LoadDanish(); - break; - - case "az": - this.LoadAzerbaijani(); - break; - - case "fi": - this.LoadFinnish(); - break; - - case "nb": - case "nn": - case "no": - this.LoadNorwegian(); - break; - - case "tr": - this.LoadTurkish(); - break; - - case "he": - this.LoadHebrew(); - break; - - case "ge": - this.LoadGreek(); - break; - - case "ko": - this.LoadKorean(); - break; - - case "lt": - this.LoadLithuanian(); - break; - - case "vi": - this.LoadVietnamese(); - break; - - case "si": - this.LoadSinhala(); - break; - - case "sl": - this.LoadSlovenian(); - break; - - case "ca": - this.LoadCatalan(); - break; - - case "et": - this.LoadEstonian(); - break; - } - - // Coerce all values - - this.RaisePropertyChanged("BackstageButtonText"); - this.RaisePropertyChanged("BackstageButtonKeyTip"); - - this.RaisePropertyChanged("MinimizeButtonScreenTipTitle"); - this.RaisePropertyChanged("MinimizeButtonScreenTipText"); - this.RaisePropertyChanged("ExpandButtonScreenTipTitle"); - this.RaisePropertyChanged("ExpandButtonScreenTipText"); - this.RaisePropertyChanged("QuickAccessToolBarDropDownButtonTooltip"); - this.RaisePropertyChanged("QuickAccessToolBarMoreControlsButtonTooltip"); - this.RaisePropertyChanged("QuickAccessToolBarMenuHeader"); - this.RaisePropertyChanged("QuickAccessToolBarMenuShowAbove"); - this.RaisePropertyChanged("QuickAccessToolBarMenuShowBelow"); - - this.RaisePropertyChanged("RibbonContextMenuAddItem"); - this.RaisePropertyChanged("RibbonContextMenuAddGroup"); - this.RaisePropertyChanged("RibbonContextMenuAddGallery"); - this.RaisePropertyChanged("RibbonContextMenuAddMenu"); - this.RaisePropertyChanged("RibbonContextMenuRemoveItem"); - this.RaisePropertyChanged("RibbonContextMenuCustomizeRibbon"); - this.RaisePropertyChanged("RibbonContextMenuCustomizeQuickAccessToolBar"); - this.RaisePropertyChanged("RibbonContextMenuShowAbove"); - this.RaisePropertyChanged("RibbonContextMenuShowBelow"); - this.RaisePropertyChanged("RibbonContextMenuMinimizeRibbon"); - - this.RaisePropertyChanged("ScreenTipDisableReasonHeader"); - this.RaisePropertyChanged("ScreenTipF1LabelHeader"); - this.RaisePropertyChanged("CustomizeStatusBar"); - } - - #endregion - - #region English - - private void LoadEnglish() - { - // Backstage button text & key tip - this.backstageButtonText = "File"; - this.backstageButtonKeyTip = "F"; - - // See right-top corner... (two different tooltips must be if you press it) - this.minimizeButtonScreenTipTitle = "Collapse the Ribbon (Ctrl+F1)"; - this.minimizeButtonScreenTipText = "Need a bit more space? Collapse the ribbon so only the tab names show."; - this.expandButtonScreenTipTitle = "Pin the Ribbon (Ctrl+F1)"; - this.expandButtonScreenTipText = "Like seeing the ribbon? Keep it open while you work."; - - // QAT tooltips and menu items - this.quickAccessToolBarDropDownButtonTooltip = "Customize Quick Access Toolbar"; - this.quickAccessToolBarMoreControlsButtonTooltip = "More controls"; // When two arrows appear ">>" - this.quickAccessToolBarMenuHeader = "Customize Quick Access Toolbar"; - this.quickAccessToolBarMenuShowAbove = "Show Above the Ribbon"; - this.quickAccessToolBarMenuShowBelow = "Show Below the Ribbon"; - - // Click on Ribbon to show context menu - this.ribbonContextMenuAddItem = "Add to Quick Access Toolbar"; // Button - this.ribbonContextMenuAddGroup = "Add Group to Quick Access Toolbar"; // For ex., by collapsed group - this.ribbonContextMenuAddGallery = "Add Gallery to Quick Access Toolbar"; // For ex., by opened font context menu - this.ribbonContextMenuAddMenu = "Add Menu to Quick Access Toolbar"; // By dashed splitter in context menu - this.ribbonContextMenuRemoveItem = "Remove from Quick Access Toolbar"; // By item in QAT - this.ribbonContextMenuCustomizeQuickAccessToolbar = "Customize Quick Access Toolbar..."; - this.ribbonContextMenuShowBelow = "Show Quick Access Toolbar Below the Ribbon"; - this.ribbonContextMenuShowAbove = "Show Quick Access Toolbar Above the Ribbon"; - this.ribbonContextMenuCustomizeRibbon = "Customize the Ribbon..."; - this.ribbonContextMenuMinimizeRibbon = "Collapse the Ribbon"; - - // To see it in Word: open *.doc (not *.docx) and see Insert->Screenshot - // (This prop was introduced after v1.3) - //Screentips - this.screenTipDisableReasonHeader = "This command is currently disabled."; - this.screenTipF1LabelHeader = "Press F1 for help"; - - // Right-click on status bar to see it. NEW! from v2.0 - this.customizeStatusBar = "Customize Status Bar"; - } - - #endregion - - #region Russian - - private void LoadRussian() - { - this.backstageButtonText = "Файл"; - this.backstageButtonKeyTip = "Ф"; - - this.minimizeButtonScreenTipTitle = "Свернуть ленту (Ctrl + F1)"; - this.minimizeButtonScreenTipText = "Отображение или скрытие ленты\n\nКогда лента скрыта, отображаются только имена вкладок."; - this.expandButtonScreenTipTitle = "Развернуть ленту (Ctrl + F1)"; - this.expandButtonScreenTipText = "Отображение или скрытие ленты\n\nКогда лента скрыта, отображаются только имена вкладок."; - - this.quickAccessToolBarDropDownButtonTooltip = "Настройка панели быстрого доступа"; - this.quickAccessToolBarMoreControlsButtonTooltip = "Другие элементы"; - this.quickAccessToolBarMenuHeader = "Настройка панели быстрого доступа"; - this.quickAccessToolBarMenuShowAbove = "Разместить над лентой"; - this.quickAccessToolBarMenuShowBelow = "Разместить под лентой"; - - this.ribbonContextMenuAddItem = "Добавить на панель быстрого доступа"; - this.ribbonContextMenuAddGroup = "Добавить группу на панель быстрого доступа"; - this.ribbonContextMenuAddGallery = "Добавить коллекцию на панель быстрого доступа"; - this.ribbonContextMenuAddMenu = "Добавить меню на панель быстрого доступа"; - this.ribbonContextMenuRemoveItem = "Удалить с панели быстрого доступа"; - this.ribbonContextMenuCustomizeQuickAccessToolbar = "Настройка панели быстрого доступа..."; - this.ribbonContextMenuShowBelow = "Разместить панель быстрого доступа под лентой"; - this.ribbonContextMenuShowAbove = "Разместить панель быстрого доступа над лентой"; - this.ribbonContextMenuCustomizeRibbon = "Настройка ленты..."; - this.ribbonContextMenuMinimizeRibbon = "Свернуть ленту"; - - // To see it in Word: open *.doc (not *.docx) and see Insert->Screenshot - this.screenTipDisableReasonHeader = "В настоящее время эта команда отключена."; - - this.customizeStatusBar = "Настройка строки состояния"; - } - - #endregion - - #region Ukrainian - - private void LoadUkrainian() - { - // Backstage button text & key tip - this.backstageButtonText = "Файл"; - this.backstageButtonKeyTip = "Ф"; - // See right-top corner... (two different tooltips must be if you press it) - this.minimizeButtonScreenTipTitle = "Сховати Стрічку (Ctrl + F1)"; - this.minimizeButtonScreenTipText = "Показати або сховати Стрічку\n\nКоли стрічка схована, видно тільки назви вкладок"; - this.expandButtonScreenTipTitle = "Показати Стрічку (Ctrl + F1)"; - this.expandButtonScreenTipText = "Показати або сховати Стрічку\n\nКоли стрічка схована, видно тільки назви вкладок"; - // QAT tooltips and menu items - this.quickAccessToolBarDropDownButtonTooltip = "Налаштувати Панель Інструментів Швидкого Доступу"; - this.quickAccessToolBarMoreControlsButtonTooltip = "Більше елементів"; // When two arrows appear ">>" - this.quickAccessToolBarMenuHeader = "Налаштувати Панель Інструментів Швидкого Доступу"; - this.quickAccessToolBarMenuShowAbove = "Показати Поверх Стрічки"; - this.quickAccessToolBarMenuShowBelow = "Показати Знизу Стрічки"; - // Click on Ribbon to show context menu - this.ribbonContextMenuAddItem = "Додати до Панелі Інструментів Швидкого Доступу"; // Button - this.ribbonContextMenuAddGroup = "Додати Групу до Панелі Інструментів Швидкого Доступу"; // For ex., by collapsed group - this.ribbonContextMenuAddGallery = "Додати Галерею до Панелі Інструментів Швидкого Доступу"; // For ex., by opened font context menu - this.ribbonContextMenuAddMenu = "Додати Меню до Панелі Інструментів Швидкого Доступу"; // By dashed splitter in context menu - this.ribbonContextMenuRemoveItem = "Видалити з Панелі Інструментів Швидкого Доступу"; // By item in QAT - this.ribbonContextMenuCustomizeQuickAccessToolbar = "Налаштувати Панель Інструментів Швидкого Доступу..."; - this.ribbonContextMenuShowBelow = "Показати Панель Інструментів Швидкого Доступу Знизу Стрічки"; - this.ribbonContextMenuShowAbove = "Показати Панель Інструментів Швидкого Доступу Поверх Стрічки"; - this.ribbonContextMenuCustomizeRibbon = "Налаштувати Стрічку..."; - this.ribbonContextMenuMinimizeRibbon = "Зменшити Стрічку"; - - // To see it in Word: open *.doc (not *.docx) and see Insert->Screenshot - // (This prop was introduced after v1.3) - this.screenTipDisableReasonHeader = "Ця команда на даний момент недоступна."; - } - - #endregion - - #region Persian - - private void LoadPersian() - { - // Backstage button text & key tip - this.backstageButtonText = "فایل"; - this.backstageButtonKeyTip = "ف"; - - // See right-top corner... (two different tooltips must be if you press it) - this.minimizeButtonScreenTipTitle = "کوچک کردن نوار (Ctrl + F1)"; - this.minimizeButtonScreenTipText = "نمایش یا مخفی کردن نوار\n\nهنگامی که نوار مخفی است، تنها\nنام زبانه ها نمایش داده می شود."; - this.expandButtonScreenTipTitle = "بزرگ کردن نوار (Ctrl + F1)"; - this.expandButtonScreenTipText = "نمایش یا مخفی کردن نوار\n\nهنگامی که نوار مخفی است، تنها\nنام زبانه ها نمایش داده می شود."; - - // QAT tooltips and menu items - this.quickAccessToolBarDropDownButtonTooltip = "دلخواه سازی میله ابزار دسترسی سریع"; - this.quickAccessToolBarMoreControlsButtonTooltip = "ابزارهای دیگر"; // When two arrows appear ">>" - this.quickAccessToolBarMenuHeader = "دلخواه سازی میله ابزار دسترسی سریع"; - this.quickAccessToolBarMenuShowAbove = "نمایش در بالای نوار"; - this.quickAccessToolBarMenuShowBelow = "نمایش در پایین نوار"; - // Click on Ribbon to show context menu - this.ribbonContextMenuAddItem = "اضافه کردن به میله ابزار دسترسی سریع"; // Button - this.ribbonContextMenuAddGroup = "اضافه کردن گروه به میله ابزار دسترسی سریع"; // For ex., by collapsed group - this.ribbonContextMenuAddGallery = "اضافه کردن گالری به میله ابزار دسترسی سریع"; // For ex., by opened font context menu - this.ribbonContextMenuAddMenu = "اضاقه کردن منو به میله ابزار دسترسی سریع"; // By dashed splitter in context menu - this.ribbonContextMenuRemoveItem = "حذف از میله ابزار دسترسی سریع"; // By item in QAT - this.ribbonContextMenuCustomizeQuickAccessToolbar = "دلخواه سازی میله ابزار دسترسی سریع..."; - this.ribbonContextMenuShowBelow = "نمایش میله ابزار دسترسی سریع در پایین نوار"; - this.ribbonContextMenuShowAbove = "نمایش میله ابزار دسترسی سریع در بالای نوار"; - this.ribbonContextMenuCustomizeRibbon = "دلخواه سازی نوار..."; - this.ribbonContextMenuMinimizeRibbon = "کوچک کردن نوار"; - } - - #endregion - - #region German - - private void LoadGerman() - { - // Backstage button text & key tip - this.backstageButtonText = "Datei"; - this.backstageButtonKeyTip = "D"; - - // See right-top corner... (two different tooltips must be if you press it) - this.minimizeButtonScreenTipTitle = "Menüband minimieren (Strg+F1)"; - this.minimizeButtonScreenTipText = "Sie benötigen etwas mehr Platz? Reduzieren Sie das Menüband, sodass nur die Registerkartennamen angezeigt werden."; - this.expandButtonScreenTipTitle = "Menüband erweitern (Strg+F1)"; - this.expandButtonScreenTipText = "Ist es Ihnen lieber, wenn Sie das Menüband sehen? Lassen Sie es während der Arbeit geöffnet."; - - // QAT tooltips and menu items - this.quickAccessToolBarDropDownButtonTooltip = "Symbolleiste für den Schnellzugriff anpassen"; - this.quickAccessToolBarMoreControlsButtonTooltip = "Weitere Befehle…"; // When two arrows appear ">>" - this.quickAccessToolBarMenuHeader = "Symbolleiste für den Schnellzugriff anpassen"; - this.quickAccessToolBarMenuShowAbove = "Über dem Menüband anzeigen"; - this.quickAccessToolBarMenuShowBelow = "Unter dem Menüband anzeigen"; - - // Click on Ribbon to show context menu - this.ribbonContextMenuAddItem = "Zur Symbolleiste für den Schnellzugriff hinzufügen"; // Button - this.ribbonContextMenuAddGroup = "Gruppe zur Symbolleiste für den Schnellzugriff hinzufügen"; // For ex., by collapsed group - this.ribbonContextMenuAddGallery = "Katalog zur Symbolleiste für den Schnellzugriff hinzufügen"; // For ex., by opened font context menu - this.ribbonContextMenuAddMenu = "Zur Symbolleiste für den Schnellzugriff hinzufügen"; // By dashed splitter in context menu - - this.ribbonContextMenuRemoveItem = "Aus Symbolleiste für den Schnellzugriff entfernen"; // By item in QAT - this.ribbonContextMenuCustomizeQuickAccessToolbar = "Symbolleiste für den Schnellzugriff anpassen..."; - this.ribbonContextMenuShowBelow = "Symbolleiste für den Schnellzugriff unter dem Menüband anzeigen"; - this.ribbonContextMenuShowAbove = "Symbolleiste für den Schnellzugriff über dem Menüband anzeigen"; - this.ribbonContextMenuCustomizeRibbon = "Menüband anpassen..."; - this.ribbonContextMenuMinimizeRibbon = "Menüband minimieren"; - - //Screentips - this.screenTipDisableReasonHeader = "Diese Funktion ist momentan deaktiviert."; - this.screenTipF1LabelHeader = "Drücken Sie F1 für die Hilfe"; - - // Right-click on status bar to see it. NEW! from v2.0 - this.customizeStatusBar = "Statusleiste anpassen"; - } - - #endregion - - #region Hungarian - - private void LoadHungarian() - { - // Backstage button text & key tip - this.backstageButtonText = "Fájl"; - this.backstageButtonKeyTip = "F"; - - // See right-top corner... (two different tooltips must be if you press it) - this.minimizeButtonScreenTipTitle = "A menüszalag összecsukása (Ctrl + F1)"; - this.minimizeButtonScreenTipText = "Csak a lapnevek megjelenítése a menüszalagon"; - this.expandButtonScreenTipTitle = "Menüszalag kibontása (Ctrl + F1)"; - this.expandButtonScreenTipText = "A menüszalag megjelenítése úgy, hogy egy parancsra kattintás után is látható maradjon"; - - // QAT tooltips and menu items - this.quickAccessToolBarDropDownButtonTooltip = "Gyorselérési eszköztár testreszabása"; - this.quickAccessToolBarMoreControlsButtonTooltip = "További vezérlők"; // When two arrows appear ">>" - this.quickAccessToolBarMenuHeader = "Gyorselérési eszköztár testreszabása"; - this.quickAccessToolBarMenuShowAbove = "Megjelenítés a menüszalag alatt"; - this.quickAccessToolBarMenuShowBelow = "Megjelenítés a menüszalag felett"; - - // Click on Ribbon to show context menu - this.ribbonContextMenuAddItem = "Felvétel a gyorselérési eszköztárra"; // Button - this.ribbonContextMenuAddGroup = "Felvétel a gyorselérési eszköztárra"; // For ex., by collapsed group - this.ribbonContextMenuAddGallery = "Gyűjtemény felvétele a gyorselérési eszköztárra"; // For ex., by opened font context menu - this.ribbonContextMenuAddMenu = "Felvétel a gyorselérési eszköztárra"; // By dashed splitter in context menu - this.ribbonContextMenuRemoveItem = "Eltávolítás a gyorselérési eszköztárról"; // By item in QAT - this.ribbonContextMenuCustomizeQuickAccessToolbar = "Gyorselérési eszköztár testreszabása..."; - this.ribbonContextMenuShowBelow = "A gyorselérési eszköztár megjelenítése a menüszalag alatt"; - this.ribbonContextMenuShowAbove = "A gyorselérési eszköztár megjelenítése a menüszalag felett"; - this.ribbonContextMenuCustomizeRibbon = "Menüszalag testreszabása..."; - this.ribbonContextMenuMinimizeRibbon = " A menüszalag összecsukása"; - - // To see it in Word: open *.doc (not *.docx) and see Insert->Screenshot - // (This prop was introduced after v1.3) - this.screenTipDisableReasonHeader = "Ez a parancs jelenleg nem használható."; - } - - #endregion - - #region Czech - - private void LoadCzech() - { - // Backstage button text & key tip - this.backstageButtonText = "Soubor"; - this.backstageButtonKeyTip = "S"; - - // See right-top corner... (two different tooltips must be if you press it) - this.minimizeButtonScreenTipTitle = "Skrýt pás karet (Ctrl + F1)"; - this.minimizeButtonScreenTipText = "Zobrazit nebo skrýt pás karet\n\nJe-li pás karet skrytý, jsou zobrazeny pouze názvy karet"; - this.expandButtonScreenTipTitle = "Zobrazit pás karet (Ctrl + F1)"; - this.expandButtonScreenTipText = "Zobrazit nebo skrýt pás karet\n\nJe-li pás karet skrytý, jsou zobrazeny pouze názvy karet"; - - // QAT tooltips and menu items - this.quickAccessToolBarDropDownButtonTooltip = "Přizpůsobit panel nástrojů Rychlý přístup"; - this.quickAccessToolBarMoreControlsButtonTooltip = "Další příkazy"; // When two arrows appear ">>" - this.quickAccessToolBarMenuHeader = "Přizpůsobit panel nástrojů Rychlý přístup"; - this.quickAccessToolBarMenuShowAbove = "Zobrazit nad pásem karet"; - this.quickAccessToolBarMenuShowBelow = "Zobrazit pod pásem karet"; - - // Click on Ribbon to show context menu - this.ribbonContextMenuAddItem = "Přidat na panel nástrojů Rychlý přístup"; // Button - this.ribbonContextMenuAddGroup = "Přidat na panel nástrojů Rychlý přístup"; // For ex., by collapsed group - this.ribbonContextMenuAddGallery = "Přidat galerii na panel nástrojů Rychlý přístup"; // For ex., by opened font context menu - this.ribbonContextMenuAddMenu = "Přidat na panel nástrojů Rychlý přístup"; // By dashed splitter in context menu - this.ribbonContextMenuRemoveItem = "Odebrat z panelu nástrojů Rychlý přístup"; // By item in QAT - this.ribbonContextMenuCustomizeQuickAccessToolbar = "Přizpůsobit panel nástrojů Rychlý přístup..."; - this.ribbonContextMenuShowBelow = "Zobrazit panel nástrojů Rychlý přístup pod pásem karet"; - this.ribbonContextMenuShowAbove = "Zobrazit panel nástrojů Rychlý přístup nad pásem karet"; - this.ribbonContextMenuCustomizeRibbon = "Přizpůsobit pás karet..."; - this.ribbonContextMenuMinimizeRibbon = "Skrýt pás karet"; - - //Screentips - this.screenTipDisableReasonHeader = "Tento příkaz je aktuálně zakázán."; - this.screenTipF1LabelHeader = "Stiskni F1 pro nápovědu"; - - // Right-click on status bar to see it. NEW! from v2.0 - this.customizeStatusBar = "Přizpůsobit Status Bar"; - } - - #endregion - - #region French - - private void LoadFrench() - { - // Backstage button text & key tip - this.backstageButtonText = "Fichier"; - this.backstageButtonKeyTip = "F"; - - // See right-top corner... (two different tooltips must be if you press it) - this.minimizeButtonScreenTipTitle = "Minimiser le Ruban (Ctrl + F1)"; - this.minimizeButtonScreenTipText = "Afficher ou masquer le Ruban \n\nQuand le Ruban est masqué, seul les noms sont affichés"; - this.expandButtonScreenTipTitle = "Agrandir le Ruban (Ctrl + F1)"; - this.expandButtonScreenTipText = "Afficher ou masquer le Ruban \n\nQuand le Ruban est masqué, seul les noms sont affichés"; - - // QAT tooltips and menu items - this.quickAccessToolBarDropDownButtonTooltip = "Personnaliser la barre d'outils Accès Rapide"; - this.quickAccessToolBarMoreControlsButtonTooltip = "Plus de contrôles"; // When two arrows appear ">>" - this.quickAccessToolBarMenuHeader = "Personnaliser la barre d'outil Accès Rapide"; - this.quickAccessToolBarMenuShowAbove = "Afficher au dessus du Ruban"; - this.quickAccessToolBarMenuShowBelow = "Afficher en dessous du Ruban"; - - // Click on Ribbon to show context menu - this.ribbonContextMenuAddItem = "Ajouter un élément à la barre d'outils Accès Rapide"; // Button - this.ribbonContextMenuAddGroup = "Ajouter un groupe à la barre d'outils Accès Rapide"; // For ex., by collapsed group - this.ribbonContextMenuAddGallery = "Ajouter une galerie à la barre d'outils Accès Rapide"; // For ex., by opened font context menu - this.ribbonContextMenuAddMenu = "Ajouter un menu à la barre d'outils Accès Rapide"; // By dashed splitter in context menu - this.ribbonContextMenuRemoveItem = "Supprimer de la barre d'outils Accès Rapide"; // By item in QAT - this.ribbonContextMenuCustomizeQuickAccessToolbar = "Personnaliser la barre d'outils Accès Rapide..."; - this.ribbonContextMenuShowBelow = "Afficher la barre d'outils Accès Rapide en dessous du Ruban"; - this.ribbonContextMenuShowAbove = "Afficher la barre d'outils Accès Rapide au dessus du Ruban"; - this.ribbonContextMenuCustomizeRibbon = "Personnaliser le Ruban..."; - this.ribbonContextMenuMinimizeRibbon = "Minimiser le Ruban"; - this.customizeStatusBar = "Personnaliser la barre de statut"; - - // To see it in Word: open *.doc (not *.docx) and see Insert->Screenshot - this.screenTipDisableReasonHeader = "Cette commande est actuellement désactivée."; - } - - #endregion - - #region Polish - - private void LoadPolish() - { - // Backstage button text & key tip - this.backstageButtonText = "Plik"; - this.backstageButtonKeyTip = "P"; - - // See right-top corner... (two different tooltips must be if you press it) - this.minimizeButtonScreenTipTitle = "Minimalizuj Wstążkę (Ctrl + F1)"; - this.minimizeButtonScreenTipText = "Pokazuje lub ukrywa Wstążkę\n\nGdy Wstążka jest ukryta, tylko nazwy zakładek są widoczne"; - this.expandButtonScreenTipTitle = "Rozwiń Wstążkę (Ctrl + F1)"; - this.expandButtonScreenTipText = "Pokazuje lub ukrywa Wstążkę\n\nGdy Wstążka jest ukryta, tylko nazwy zakładek są widoczne"; - - // QAT tooltips and menu items - this.quickAccessToolBarDropDownButtonTooltip = "Dostosuj pasek narzędzi Szybki dostęp"; - this.quickAccessToolBarMoreControlsButtonTooltip = "Więcej poleceń..."; // When two arrows appear ">>" - this.quickAccessToolBarMenuHeader = "Dostosuj pasek narzędzi Szybki dostęp"; - this.quickAccessToolBarMenuShowAbove = "Pokaż powyżej Wstążki"; - this.quickAccessToolBarMenuShowBelow = "Pokaż poniżej Wstążki"; - - // Click on Ribbon to show context menu - this.ribbonContextMenuAddItem = "Dodaj do paska narzędzi Szybki dostęp"; // Button - this.ribbonContextMenuAddGroup = "Dodaj Grupę do paska narzędzi Szybki dostęp"; // For ex., by collapsed group - this.ribbonContextMenuAddGallery = "Dodaj Galerię do paska narzędzi Szybki dostęp"; // For ex., by opened font context menu - this.ribbonContextMenuAddMenu = "Dodaj do paska narzędzi Szybki dostęp"; // By dashed splitter in context menu - this.ribbonContextMenuRemoveItem = "Usuń z paska narzędzi Szybki dostęp"; // By item in QAT - this.ribbonContextMenuCustomizeQuickAccessToolbar = "Dostosuj pasek narzędzi Szybki dostęp..."; - this.ribbonContextMenuShowBelow = "Pokaż pasek Szybki dostęp poniżej Wstążki"; - this.ribbonContextMenuShowAbove = "Pokaż pasek Szybki dostęp powyżej Wstążki"; - this.ribbonContextMenuCustomizeRibbon = "Dostosuj Wstążkę..."; - this.ribbonContextMenuMinimizeRibbon = "Minimalizuj Wstążkę"; - } - - #endregion - - #region Japanese - - private void LoadJapanese() - { - // Backstage button text & key tip - this.backstageButtonText = "ファイル"; - this.backstageButtonKeyTip = "F"; - - // See right-top corner... (two different tooltips must be if you press it) - this.minimizeButtonScreenTipTitle = "リボンの最小化 (Ctrl + F1)"; - this.minimizeButtonScreenTipText = "リボンの表示/非表示を切り替えます。\n\nリボンを非表示にすると、タブ名のみが表示されます。"; - this.expandButtonScreenTipTitle = "リボンの展開 (Ctrl + F1)"; - this.expandButtonScreenTipText = "リボンの表示/非表示を切り替えます。\n\nリボンを非表示にすると、タブ名のみが表示されます。"; - - // QAT tooltips and menu items - this.quickAccessToolBarDropDownButtonTooltip = "クイック アクセス ツール バーのユーザー設定"; - this.quickAccessToolBarMoreControlsButtonTooltip = "その他のボタン"; // When two arrows appear ">>" - this.quickAccessToolBarMenuHeader = "クイック アクセス ツール バーのユーザー設定"; - this.quickAccessToolBarMenuShowAbove = "リボンの上に表示"; - this.quickAccessToolBarMenuShowBelow = "リボンの下に表示"; - - // Click on Ribbon to show context menu - this.ribbonContextMenuAddItem = "クイック アクセス ツール バーに追加"; // Button - this.ribbonContextMenuAddGroup = "グループをクイック アクセス ツール バーに追加"; // For ex., by collapsed group - this.ribbonContextMenuAddGallery = "ギャラリーをクイック アクセス ツール バーに追加"; // For ex., by opened font context menu - this.ribbonContextMenuAddMenu = "メニューをクイック アクセス ツール バーに追加"; // By dashed splitter in context menu - this.ribbonContextMenuRemoveItem = "クイック アクセス ツール バーから削除"; // By item in QAT - this.ribbonContextMenuCustomizeQuickAccessToolbar = "クイック アクセス ツール バーのユーザー設定..."; - this.ribbonContextMenuShowBelow = "クイック アクセス ツール バーをリボンの下に表示"; - this.ribbonContextMenuShowAbove = "クイック アクセス ツール バーをリボンの上に表示"; - this.ribbonContextMenuCustomizeRibbon = "リボンのユーザー設定..."; - this.ribbonContextMenuMinimizeRibbon = "リボンの最小化"; - this.customizeStatusBar = "ステータス バーのユーザー設定"; - - this.screenTipDisableReasonHeader = "このコマンドは現在無効になっています"; - } - - #endregion - - #region Dutch - - private void LoadDutch() - { - // Backstage button text & key tip - this.backstageButtonText = "Bestand"; - this.backstageButtonKeyTip = "B"; - // See right-top corner... (two different tooltips must be if you press it) - this.minimizeButtonScreenTipTitle = "Het lint minimaliseren (Ctrl + F1)"; - this.minimizeButtonScreenTipText = "Verberg of laat het lint zien\n\nWanneer het lint verborgen is, zijn alleen de tabulatie namen zichtbaar"; - this.expandButtonScreenTipTitle = "Het lint Maximaliseren (Ctrl + F1)"; - this.expandButtonScreenTipText = "Verberg of laat het lint zien\n\nWanneer het lint verborgen is, zijn alleen de tabulatie namen zichtbaar"; - - // QAT tooltips and menu items - this.quickAccessToolBarDropDownButtonTooltip = "Werkbalk snelle toegang aanpassen"; - this.quickAccessToolBarMoreControlsButtonTooltip = "meer opdrachten"; // When two arrows appear ">>" - this.quickAccessToolBarMenuHeader = " Werkbalk snelle toegang aanpassen "; - this.quickAccessToolBarMenuShowAbove = "Boven het lint weergeven"; - this.quickAccessToolBarMenuShowBelow = "beneden het lint weergeven"; - // Click on Ribbon to show context menu - this.ribbonContextMenuAddItem = "Menu toevoegen aan werkbalk snelle toegang"; // Button - this.ribbonContextMenuAddGroup = "Groep toevoegen aan werkbalk snelle toegang"; // For ex., by collapsed group - this.ribbonContextMenuAddGallery = "Galerij toevoegen aan werkbalk snelle toegang"; // For ex., by opened font context menu - this.ribbonContextMenuAddMenu = " Menu toevoegen aan werkbalk snelle toegang "; // By dashed splitter in context menu - this.ribbonContextMenuRemoveItem = " Verwijder uit werkbalk snelle toegang "; // By item in QAT - this.ribbonContextMenuCustomizeQuickAccessToolbar = "Customize Quick Access Toolbar..."; - this.ribbonContextMenuShowBelow = " Werkbalk snelle toegang onder het lint weergeven"; - this.ribbonContextMenuShowAbove = " Werkbalk snelle toegang boven het lint weergeven "; - this.ribbonContextMenuCustomizeRibbon = "Lint aanpassen..."; - this.ribbonContextMenuMinimizeRibbon = " Het lint minimaliseren"; - } - - #endregion - - #region Brazilian - - private void LoadPortugueseBrazilian() - { - // Backstage button text & key tip - this.backstageButtonText = "Arquivo"; - this.backstageButtonKeyTip = "A"; - - // See right-top corner... (two different tooltips must be if you press it) - this.minimizeButtonScreenTipTitle = "Minimizar o Ribbon (Ctrl + F1)"; - this.minimizeButtonScreenTipText = "Mostrar ou esconder o Ribbon\n\nQuando o Ribbon estiver escondido, somente o nome das abas serão mostrados"; - this.expandButtonScreenTipTitle = "Expandir o Ribbon (Ctrl + F1)"; - this.expandButtonScreenTipText = "Mostrar ou esconder o Ribbon\n\nQuando o Ribbon estiver escondido, somente o nome das abas serão mostrados"; - - // QAT tooltips and menu items - this.quickAccessToolBarDropDownButtonTooltip = "Customizar Barra de acesso rápido"; - this.quickAccessToolBarMoreControlsButtonTooltip = "Mais controles"; // When two arrows appear ">>" - this.quickAccessToolBarMenuHeader = " Customizar Barra de acesso rápido"; - this.quickAccessToolBarMenuShowAbove = "Mostrar acima do Ribbon"; - this.quickAccessToolBarMenuShowBelow = "Mostrar abaixo do Ribbon"; - - // Click on Ribbon to show context menu - this.ribbonContextMenuAddItem = "Adicionar para Barra de acesso rápido"; // Button - this.ribbonContextMenuAddGroup = " Adicionar o grupo para Barra de acesso rápido"; // For ex., by collapsed group - this.ribbonContextMenuAddGallery = "Adicionar a galeria para Barra de acesso rápido"; - // For ex., by opened font context menu - this.ribbonContextMenuAddMenu = " Adicionar o menu para Barra de acesso rápido"; - // By dashed splitter in context menu - this.ribbonContextMenuRemoveItem = "Remover da Barra de acesso rápido"; // By item in QAT - this.ribbonContextMenuCustomizeQuickAccessToolbar = "Customizar Barra de acesso rápido..."; - this.ribbonContextMenuShowBelow = "Mostrar Barra de acesso rápido abaixo do Ribbon"; - this.ribbonContextMenuShowAbove = "Mostrar Barra de acesso rápido acima do Ribbon"; - this.ribbonContextMenuCustomizeRibbon = "Customizar o Ribbon..."; - this.ribbonContextMenuMinimizeRibbon = "Minimizar o Ribbon"; - - // To see it in Word: open *.doc (not *.docx) and see Insert->Screenshot - // (This prop was introduced after v1.3) - this.screenTipDisableReasonHeader = "Este comando está desativado."; - } - - #endregion - - #region Spanish - - private void LoadSpanish() - { - // Backstage button text & key tip - this.backstageButtonText = "Archivo"; - this.backstageButtonKeyTip = "A"; - - // See right-top corner... (two different tooltips must be if you press it) - // TRANSLATOR'S NOTE: This block is not shown at Windows 7's Apps (WordPad or Paint) - this.minimizeButtonScreenTipTitle = "Minimizar la cinta (Ctrl + F1)"; - this.minimizeButtonScreenTipText = "Muestra u oculta la cinta\n\nCuando la cinta está oculta, sólo se muestran los nombres de las pestañas"; - this.expandButtonScreenTipTitle = "Expandir la cinta (Ctrl + F1)"; - this.expandButtonScreenTipText = "Muestra u oculta la cinta\n\nCuando la cinta está oculta, sólo se muestran los nombres de las pestañas"; - - // QAT tooltips and menu items - this.quickAccessToolBarDropDownButtonTooltip = "Personalizar barra de herramientas de acceso rápido"; - this.quickAccessToolBarMoreControlsButtonTooltip = "Más controles"; // When two arrows appear ">>" - this.quickAccessToolBarMenuHeader = "Personalizar barra de herramientas de acceso rápido"; - this.quickAccessToolBarMenuShowAbove = "Mostrar sobre la cinta"; - this.quickAccessToolBarMenuShowBelow = "Mostrar bajo la cinta"; - - // Click on Ribbon to show context menu - this.ribbonContextMenuAddItem = "Agregar a la barra de herramientas de acceso rápido"; // Button - this.ribbonContextMenuAddGroup = "Agregar grupo a la barra de herramientas de acceso rápido"; // For ex., by collapsed group - this.ribbonContextMenuAddGallery = "Agregar galería a la barra de herramientas de acceso rápido"; // For ex., by opened font context menu - this.ribbonContextMenuAddMenu = "Agregar menú a la barra de herramientas de acceso rápido"; // By dashed splitter in context menu - this.ribbonContextMenuRemoveItem = "Quitar de la barra de herramientas de acceso rápido"; // By item in QAT - this.ribbonContextMenuCustomizeQuickAccessToolbar = "Personalizar la barra de herramientas de acceso rápido..."; - this.ribbonContextMenuShowBelow = "Mostrar barra de herramientas de acceso rápido bajo la cinta"; - this.ribbonContextMenuShowAbove = "Mostrar barra de herramientas de acceso rápido sobre la cinta"; - this.ribbonContextMenuCustomizeRibbon = "Personalizar la cinta..."; - this.ribbonContextMenuMinimizeRibbon = "Minimizar la cinta"; - - //Screentips - this.screenTipDisableReasonHeader = "Este comando está desactivado actualmente"; - this.screenTipF1LabelHeader = "Pulse F1 para obtener más ayuda"; - - // Right-click on status bar to see it. NEW! from v2.0 - this.customizeStatusBar = "Personalizar barra de estado"; - } - - #endregion - - #region Chinese - - private void LoadChinese() - { - // Backstage button text & key tip - this.backstageButtonText = "文件"; - this.backstageButtonKeyTip = "F"; - - // See right-top corner... (two different tooltips must be if you press it) - this.minimizeButtonScreenTipTitle = "功能区最小化 (Ctrl + F1)"; - this.minimizeButtonScreenTipText = "仅显示功能区上的选项卡名称。单击选项卡可显示命令。"; - this.expandButtonScreenTipTitle = "展开功能区 (Ctrl + F1)"; - this.expandButtonScreenTipText = "始终显示功能区选项卡和命令。"; - - // QAT tooltips and menu items - this.quickAccessToolBarDropDownButtonTooltip = "自定义快速访问具栏"; - this.quickAccessToolBarMoreControlsButtonTooltip = "其他命令"; // When two arrows appear ">>" - this.quickAccessToolBarMenuHeader = "自定义快速访问工具栏"; - this.quickAccessToolBarMenuShowAbove = "在功能区上方显示"; - this.quickAccessToolBarMenuShowBelow = "在功能区下方显示"; - - // Click on Ribbon to show context menu - this.ribbonContextMenuAddItem = "添加到快速访问工具栏"; // Button - this.ribbonContextMenuAddGroup = "在快速访问工具栏中添加组"; // For ex., by collapsed group - this.ribbonContextMenuAddGallery = "在快速访问工具栏中添加样式"; // For ex., by opened font context menu - this.ribbonContextMenuAddMenu = "在快速访问工具栏中添加菜单"; // By dashed splitter in context menu - this.ribbonContextMenuRemoveItem = "在快速访问工具栏中移除"; // By item in QAT - this.ribbonContextMenuCustomizeQuickAccessToolbar = "自定义快速访问工具栏..."; - this.ribbonContextMenuShowBelow = "在功能区下方显示快速访问工具栏"; - this.ribbonContextMenuShowAbove = "在功能区上方显示快速访问工具栏"; - this.ribbonContextMenuCustomizeRibbon = "自定义功能区..."; - this.ribbonContextMenuMinimizeRibbon = "功能区最小化"; - - // To see it in Word: open *.doc (not *.docx) and see Insert->Screenshot - // (This prop was introduced after v1.3) - this.screenTipDisableReasonHeader = "此命令当前已被禁用。"; - - // Right-click on status bar to see it. NEW! from v2.0 - this.customizeStatusBar = "自定义状态栏"; - } - - #endregion - - #region Swedish - - private void LoadSwedish() - { - // Backstage button text & key tip - this.backstageButtonText = "Arkiv"; - this.backstageButtonKeyTip = "A"; - - // See right-top corner... (two different tooltips must be if you press it) - this.minimizeButtonScreenTipTitle = "Minimera menyfliksområdet (Ctrl + F1)"; - this.minimizeButtonScreenTipText = "Visa eller göm menyfliksområdet \n\nNär menyfliksområdet är dolt, visas endast flikarna"; - this.expandButtonScreenTipTitle = "Expandera menyfliksområdet (Ctrl + F1)"; - this.expandButtonScreenTipText = "Visa eller göm menyfliksområdet \n\nNär menyfliksområdet är dolt, visas endast flikarna"; - - // QAT tooltips and menu items - this.quickAccessToolBarDropDownButtonTooltip = "Anpassa verktygsfältet Snabbåtkomst "; - this.quickAccessToolBarMoreControlsButtonTooltip = "Fler kommandon"; // When two arrows appear ">>" - this.quickAccessToolBarMenuHeader = " Anpassa verktygsfältet Snabbåtkomst"; - this.quickAccessToolBarMenuShowAbove = "Visa ovanför menyfliksområdet"; - this.quickAccessToolBarMenuShowBelow = "Visa under menyfliksområdet"; - - // Click on Ribbon to show context menu - this.ribbonContextMenuAddItem = "Lägg till i verktygsfältet Snabbåtkomst"; // Button - this.ribbonContextMenuAddGroup = "Lägg till i verktygsfältet Snabbåtkomst"; // For ex., by collapsed group - this.ribbonContextMenuAddGallery = "Lägg till galleriet i verktygsfältet Snabbåtkomst"; // For ex., by opened font context menu - this.ribbonContextMenuAddMenu = " Lägg till menyn i verktygsfältet Snabbåtkomst "; // By dashed splitter in context menu - this.ribbonContextMenuRemoveItem = "Ta bort från verktygsfältet Snabbåtkomst"; // By item in QAT - this.ribbonContextMenuCustomizeQuickAccessToolbar = "Anpassa verktygsfältet Snabbåtkomst..."; - this.ribbonContextMenuShowBelow = " Visa verktygsfältet Snabbåtkomst under menyfliksområdet"; - this.ribbonContextMenuShowAbove = " Visa verktygsfältet Snabbåtkomst ovanför menyfliksområdet "; - this.ribbonContextMenuCustomizeRibbon = "Anpassa menyfliksområdet..."; - this.ribbonContextMenuMinimizeRibbon = "Minimera menyfliksområdet"; - } - - #endregion - - #region Slovak - - private void LoadSlovak() - { - // Backstage button text & key tip - this.backstageButtonText = "Súbor"; - this.backstageButtonKeyTip = "S"; - - // See right-top corner... (two different tooltips must be if you press it) - this.minimizeButtonScreenTipTitle = "Skryť pás s nástrojmi (Ctrl + F1)"; - this.minimizeButtonScreenTipText = "Zobraziť alebo skryť pás s nástrojmi\n\nKeď je pás s nástrojmi skrytý, sú zobrazené iba názvy kariet"; - this.expandButtonScreenTipTitle = "Zobraziť pás s nástrojmi (Ctrl + F1)"; - this.expandButtonScreenTipText = " Zobraziť alebo skryť pás s nástrojmi\n\nKeď je pás s nástrojmi skrytý, sú zobrazené iba názvy kariet "; - - // QAT tooltips and menu items - this.quickAccessToolBarDropDownButtonTooltip = "Prispôsobenie panela s nástrojmi Rýchly prístup"; - this.quickAccessToolBarMoreControlsButtonTooltip = "Ďalšie príkazy"; // When two arrows appear ">>" - this.quickAccessToolBarMenuHeader = "Prispôsobenie panela s nástrojmi Rýchly prístup"; - this.quickAccessToolBarMenuShowAbove = " Zobraziť nad pásom s nástrojmi "; - this.quickAccessToolBarMenuShowBelow = "Zobraziť pod pásom s nástrojmi"; - - // Click on Ribbon to show context menu - this.ribbonContextMenuAddItem = "Pridať na panel s nástrojmi Rýchly prístup"; // Button - this.ribbonContextMenuAddGroup = " Pridať na panel s nástrojmi Rýchly prístup "; // For ex., by collapsed group - this.ribbonContextMenuAddGallery = " Pridať galériu do panela s nástrojmi Rýchly prístup "; // For ex., by opened font context menu - this.ribbonContextMenuAddMenu = "Pridať na panel s nástrojmi Rýchly prístup"; // By dashed splitter in context menu - this.ribbonContextMenuRemoveItem = "Odstrániť z panela s nástrojmi Rýchly prístup "; // By item in QAT - this.ribbonContextMenuCustomizeQuickAccessToolbar = " Prispôsobenie panela s nástrojmi Rýchly prístup..."; - this.ribbonContextMenuShowBelow = "Panel s nástrojmi Rýchly prístup zobraziť pod panelom s nástrojmi"; - this.ribbonContextMenuShowAbove = "Panel s nástrojmi Rýchly prístup zobraziť nad panelom s nástrojmi "; - this.ribbonContextMenuCustomizeRibbon = "Prispôsobenie panela s nástrojmi Rýchly prístup..."; - this.ribbonContextMenuMinimizeRibbon = "Minimalizovať pás s nástrojmi"; - } - - #endregion - - #region Romanian - - private void LoadRomanian() - { - // Backstage button text & key tip - this.backstageButtonText = "Fișier"; - this.backstageButtonKeyTip = "F"; - - // See right-top corner... (two different tooltips must be if you press it) - this.minimizeButtonScreenTipTitle = "Minimizează Ribbon-ul (Ctrl + F1)"; - this.minimizeButtonScreenTipText = "Afișează sau ascunde Ribbon-ul\nCând Ribbon-ul este ascuns, sunt afișate doar numele taburilor"; - this.expandButtonScreenTipTitle = "Expandează Ribbon-ul (Ctrl + F1)"; - this.expandButtonScreenTipText = "Afișează sau ascunde Ribbon-ul\nCând Ribbon-ul este ascuns, sunt afișate doar numele taburilor"; - - // QAT tooltips and menu items - this.quickAccessToolBarDropDownButtonTooltip = "Personalizează Bara de Acces Rapid"; - this.quickAccessToolBarMoreControlsButtonTooltip = "Mai multe controale"; // When two arrows appear ">>" - this.quickAccessToolBarMenuHeader = "Personalizează Bara de Acces Rapid"; - this.quickAccessToolBarMenuShowAbove = "Afișează peste Ribbon"; - this.quickAccessToolBarMenuShowBelow = "Afișează sub Ribbon"; - - // Click on Ribbon to show context menu - this.ribbonContextMenuAddItem = "Adaugă la Bara de Acess Rapid"; // Button - this.ribbonContextMenuAddGroup = "Adaugă Grupul la Bara de Acess Rapid"; // For ex., by collapsed group - this.ribbonContextMenuAddGallery = "Adaugă Galeria la Bara de Acess Rapid"; - // For ex., by opened font context menu - this.ribbonContextMenuAddMenu = "Adaugă Meniul la Bara de Acess Rapid"; // By dashed splitter in context menu - this.ribbonContextMenuRemoveItem = "Eimină din Bara de Acess Rapid"; // By item in QAT - this.ribbonContextMenuCustomizeQuickAccessToolbar = "Personalizează Bara de Acces Rapid..."; - this.ribbonContextMenuShowBelow = "Afișează Bara de Acces Rapid sub Ribbon"; - this.ribbonContextMenuShowAbove = "Afișează Bara de Acces Rapid peste Ribbon"; - this.ribbonContextMenuCustomizeRibbon = "Personalizează Ribbon-ul..."; - this.ribbonContextMenuMinimizeRibbon = "Minimizează Ribbon-ul..."; - - // To see it in Word: open *.doc (not *.docx) and see Insert->Screenshot - // (This prop was introduced after v1.3) - this.screenTipDisableReasonHeader = "Această comandă nu este disponibilă momentan."; - } - - #endregion - - #region Italian - - private void LoadItalian() - { - // Backstage button text & key tip - this.backstageButtonText = "File"; - this.backstageButtonKeyTip = "F"; - - // See right-top corner... (two different tooltips must be if you press it) - this.minimizeButtonScreenTipTitle = "Riduci a icona barra multifunzione (Ctrl + F1)"; - this.minimizeButtonScreenTipText = "Consente di visualizzare solo i nomi delle schede nella barra multifunzione."; - this.expandButtonScreenTipTitle = "Espandi la barra multifunzione (Ctrl + F1)"; - this.expandButtonScreenTipText = "Visualizza la barra multifunzione in modo che rimanga sempre espansa, anche se l’utente ha fatto click su un comando."; - - // QAT tooltips and menu items - this.quickAccessToolBarDropDownButtonTooltip = "Personalizza barra di accesso rapido"; - this.quickAccessToolBarMoreControlsButtonTooltip = "Altri comandi…"; // When two arrows appear ">>" - this.quickAccessToolBarMenuHeader = "Personalizza barra di accesso rapido"; - this.quickAccessToolBarMenuShowAbove = "Mostra sopra la barra multifunzione"; - this.quickAccessToolBarMenuShowBelow = "Mostra sotto la barra multifunzione"; - - // Click on Ribbon to show context menu - this.ribbonContextMenuAddItem = "Aggiungi alla barra di accesso rapido"; // Button - this.ribbonContextMenuAddGroup = "Aggiungi gruppo alla barra di accesso rapido"; // For ex., by collapsed group - this.ribbonContextMenuAddGallery = "Aggiungi raccolta alla barra di accesso rapido"; // For ex., by opened font context menu - this.ribbonContextMenuAddMenu = "Aggiungi menu alla barra di accesso rapido"; // By dashed splitter in context menu - this.ribbonContextMenuRemoveItem = "Rimuovi dalla barra di accesso rapido"; // By item in QAT - this.ribbonContextMenuCustomizeQuickAccessToolbar = "Personalizza barra di accesso rapido..."; - this.ribbonContextMenuShowBelow = "Mostra la barra di accesso rapido sotto la barra multifunzione"; - this.ribbonContextMenuShowAbove = "Mostra la barra di accesso rapido sopra la barra multifunzione"; - this.ribbonContextMenuCustomizeRibbon = "Personalizza barra multifunzione..."; - this.ribbonContextMenuMinimizeRibbon = "Riduci a icona barra multifunzione"; - - // To see it in Word: open *.doc (not *.docx) and see Insert->Screenshot - // (This prop was introduced after v1.3) - this.screenTipDisableReasonHeader = "Questo commando è disattivato."; - } - - #endregion - - #region Arabic - - private void LoadArabic() - { - // Backstage button text & key tip - this.backstageButtonText = "ملف "; - this.backstageButtonKeyTip = "م "; - - // See right-top corner... (two different tooltips must be if you press it) - this.minimizeButtonScreenTipTitle = "(Ctrl + F1)تصغير الشريط "; - this.minimizeButtonScreenTipText = "إظهار أسماء علامات التبويب فقط على الشريط."; - this.expandButtonScreenTipTitle = "(Ctrl + F1)توسيع الشريط "; - this.expandButtonScreenTipText = "إظهار الشريط بحيث يكون موسعاً دائماً حتى بعد النقر فوق أمر."; - - // QAT tooltips and menu items - this.quickAccessToolBarDropDownButtonTooltip = "تخصيص شريط أدوات الوصول السريع"; - this.quickAccessToolBarMoreControlsButtonTooltip = "أوامر إضافية"; // When two arrows appear ">>" - this.quickAccessToolBarMenuHeader = "تخصيص شريط أدوات الوصول السريع"; - this.quickAccessToolBarMenuShowAbove = "إظهار أعلى الشريط"; - this.quickAccessToolBarMenuShowBelow = "إظهار أسفل الشريط"; - - // Click on Ribbon to show context menu - this.ribbonContextMenuAddItem = "إضافة إلى شريط أدوات الوصول السريع"; // Button - this.ribbonContextMenuAddGroup = "إضافة إلى شريط أدوات الوصول السريع"; // For ex., by collapsed group - this.ribbonContextMenuAddGallery = "إضافة إلى شريط أدوات الوصول السريع"; // For ex., by opened font context menu - this.ribbonContextMenuAddMenu = "إضافة إلى شريط أدوات الوصول السريع"; // By dashed splitter in context menu - this.ribbonContextMenuRemoveItem = "إزالة إلى شريط أدوات الوصول السريع"; // By item in QAT - this.ribbonContextMenuCustomizeQuickAccessToolbar = "تخصيص شريط أدوات الوصول السريع..."; - this.ribbonContextMenuShowBelow = "إظهار شريط أدوات الوصول السريع أسفل الشريط"; - this.ribbonContextMenuShowAbove = "إظهار شريط أدوات الوصول السريع أعلى الشريط"; - this.ribbonContextMenuCustomizeRibbon = "تخصيص الشريط..."; - this.ribbonContextMenuMinimizeRibbon = "تصغير الشريط"; - - // To see it in Word: open *.doc (not *.docx) and see Insert->Screenshot - // (This prop was introduced after v1.3) - this.screenTipDisableReasonHeader = "تم حالياً تعطيل هذا الأمر."; - } - - #endregion - - #region Danish - - private void LoadDanish() - { - // Backstage button text & key - this.backstageButtonText = "Filer"; - this.backstageButtonKeyTip = "F"; - - // See right-top corner... (two different tooltips must be if you press it) - this.minimizeButtonScreenTipTitle = "Minimer båndet (Ctrl + F1)"; - this.minimizeButtonScreenTipText = "Vis kun fanenavnene på båndet."; - this.expandButtonScreenTipTitle = "Udvid båndet (Ctrl + F1)"; - this.expandButtonScreenTipText = "Vis båndet, så det altid er udvidet, selv når du klikker på en kommando."; - - // QAT tooltips and menu items - this.quickAccessToolBarDropDownButtonTooltip = "Tilpas værktøjslinjen Hurtig adgang"; - this.quickAccessToolBarMoreControlsButtonTooltip = "Flere kontrolelementer"; // When two arrows appear ">>" - this.quickAccessToolBarMenuHeader = " Tilpas værktøjslinjen Hurtig adgang"; - this.quickAccessToolBarMenuShowAbove = "Vis ovenover båndet"; - this.quickAccessToolBarMenuShowBelow = "Vis under båndet"; - - // Click on Ribbon to show context menu - this.ribbonContextMenuAddItem = "Føj til værktøjslinjen Hurtig adgang"; // Button - this.ribbonContextMenuAddGroup = "Føj til værktøjslinjen Hurtig adgang"; // For ex., by collapsed group - this.ribbonContextMenuAddGallery = "Tilføj Galleri til værktøjslinjen Hurtig adgang"; // For ex., by opened font context menu - this.ribbonContextMenuAddMenu = "Føj til værktøjslinjen Hurtig adgang"; // By dashed splitter in context menu - this.ribbonContextMenuRemoveItem = "Fjern fra værktøjslinjen Hurtig adgang"; // By item in QAT - this.ribbonContextMenuCustomizeQuickAccessToolbar = "Tilpas værktøjslinjen Hurtig adgang..."; - this.ribbonContextMenuShowBelow = "Vis værktøjslinjen Hurtig adgang under båndet"; - this.ribbonContextMenuShowAbove = "Vis værktøjslinjen Hurtig adgang ovenover båndet"; - this.ribbonContextMenuCustomizeRibbon = "Tilpas båndet..."; - this.ribbonContextMenuMinimizeRibbon = "Minimer båndet"; - - // To see it in Word: open *.doc (not *.docx) and see Insert->Screenshot - // (This prop was introduced after v1.3) - this.screenTipDisableReasonHeader = "Denne kommando er aktuelt deaktiveret."; - } - - #endregion - - #region Portuguese - - private void LoadPortuguese() - { - // Backstage button text & key tip - this.backstageButtonText = "Ficheiro"; - this.backstageButtonKeyTip = "F"; - - // See right-top corner... (two different tooltips must be if you press it) - this.minimizeButtonScreenTipTitle = "Minimizar o Friso (Ctrl + F1)"; - this.minimizeButtonScreenTipText = "Mostrar apenas os nomes dos separadores no Frisos."; - this.expandButtonScreenTipTitle = "Expandir o Friso (Ctrl + F1)"; - this.expandButtonScreenTipText = "Mostrar o Friso de modo a aparecer sempre expandido mesmo depois de clicar num comando."; - - // QAT tooltips and menu items - this.quickAccessToolBarDropDownButtonTooltip = "Personalizar Barra de Ferramentas de Acesso Rápido"; - this.quickAccessToolBarMoreControlsButtonTooltip = "Mais Comandos..."; // When two arrows appear ">>" - this.quickAccessToolBarMenuHeader = "Personalizar Barra de Ferramentas de Acesso Rápido"; - this.quickAccessToolBarMenuShowAbove = "Mostrar Acima do Friso"; - this.quickAccessToolBarMenuShowBelow = "Mostrar Abaixo do Friso"; - - // Click on Ribbon to show context menu - this.ribbonContextMenuAddItem = "Adicionar à Barra de Ferramentas de Acesso Rápido"; - this.ribbonContextMenuAddGroup = "Adicionar Grupo à Barra de Ferramentas de Acesso Rápido"; - this.ribbonContextMenuAddGallery = "Adicionar Galeria à Barra de Ferramentas de Acesso Rápido"; - this.ribbonContextMenuAddMenu = "Adicionar Menu à Barra de Ferramentas de Acesso Rápido"; - this.ribbonContextMenuRemoveItem = "Remover da Barra de Ferramentas de Acesso Rápido"; - this.ribbonContextMenuCustomizeQuickAccessToolbar = "Personalizar Barra de Ferramentas de Acesso Rápido..."; - this.ribbonContextMenuShowBelow = "Mostrar Barra de Ferramentas de Acesso Rápido Abaixo do Friso"; - this.ribbonContextMenuShowAbove = "Mostrar Barra de Ferramentas de Acesso Rápido Acima do Friso"; - this.ribbonContextMenuCustomizeRibbon = "Personalizar o Friso..."; - this.ribbonContextMenuMinimizeRibbon = "Minimizar o Friso"; - - // To see it in Word: open *.doc (not *.docx) and see Insert->Screenshot - // (This prop was introduced after v1.3) - this.screenTipDisableReasonHeader = "Este comando está desactivado actualmente."; - } - - #endregion - - #region Azerbaijani - - private void LoadAzerbaijani() - { - // Backstage button text & key tip - this.backstageButtonText = "Fayl"; - this.backstageButtonKeyTip = "F"; - - // See right-top corner... (two different tooltips must be if you press it) - this.minimizeButtonScreenTipTitle = "Menyu lentini kiçilt (Ctrl + F1)"; - this.minimizeButtonScreenTipText = "Menyu lentini göstər və ya gizlət\n\nMenyu lentini kiçiləndə, yalnız tabların adları göstərilir"; - this.expandButtonScreenTipTitle = "Menyu lentini böyüt(Ctrl + F1)"; - this.expandButtonScreenTipText = " Menyu lentini göstər və ya gizlət\n\nMenyu lentini gizldəndə, yalnız, tabların adları göstərilir"; - - // QAT tooltips and menu items - this.quickAccessToolBarDropDownButtonTooltip = "Sürətli Keçidin Alətlərini fərdiləşdir"; - this.quickAccessToolBarMoreControlsButtonTooltip = "Digər nəzarət vasitələri"; // When two arrows appear ">>" - this.quickAccessToolBarMenuHeader = " Sürətli Keçidin Alətlərini fərdiləşdir "; - this.quickAccessToolBarMenuShowAbove = "Menyu lentinin üstündə göstər"; - this.quickAccessToolBarMenuShowBelow = " Menyu lentinin altında göstər "; - - // Click on Ribbon to show context menu - this.ribbonContextMenuAddItem = "Sürətli Keçidin Alətlərinə əlavə et"; // Button - this.ribbonContextMenuAddGroup = " Sürətli Keçidin Alətlərinə Qrup əlavə et "; // For ex., by collapsed group - this.ribbonContextMenuAddGallery = " Sürətli Keçidin Alətlərinə Qalereya əlavə et"; // For ex., by opened font context menu - this.ribbonContextMenuAddMenu = " Sürətli Keçidin Alətlərinə Menyu əlavə et"; // By dashed splitter in context menu - this.ribbonContextMenuRemoveItem = " Sürətli Keçidin Alətlərindən sil"; // By item in QAT - this.ribbonContextMenuCustomizeQuickAccessToolbar = " Sürətli Keçidin Alətlərini fərdiləşdir..."; - this.ribbonContextMenuShowBelow = " Sürətli Keçidin Alətlərini Menyu lentinin altında göstər "; - this.ribbonContextMenuShowAbove = " Sürətli Keçidin Alətlərini Menyu lentinin üstündə göstər "; - this.ribbonContextMenuCustomizeRibbon = "Menyu lentini fərdiləşdir..."; - this.ribbonContextMenuMinimizeRibbon = " Menyu lentini kiçilt"; - } - - #endregion - - #region Finnish - - private void LoadFinnish() - { - this.backstageButtonText = "Tiedosto"; - this.backstageButtonKeyTip = "T"; - this.minimizeButtonScreenTipTitle = "Pienennä valintanauha (Ctrl + F1)"; - this.minimizeButtonScreenTipText = "Näytä valintanauhassa vain välilehtien nimet"; - this.expandButtonScreenTipTitle = "Laajenna valintanauha (Ctrl + F1)"; - this.expandButtonScreenTipText = "Näytä valintanauha aina laajennettuna silloinkin, kun valitset komennon"; - this.quickAccessToolBarDropDownButtonTooltip = "Mukauta pikatyökaluriviä"; - this.quickAccessToolBarMoreControlsButtonTooltip = "Lisää valintoja"; - this.quickAccessToolBarMenuHeader = "Mukauta pikatyökaluriviä"; - this.quickAccessToolBarMenuShowAbove = "Näytä valintanauhan yläpuolella"; - this.quickAccessToolBarMenuShowBelow = "Näytä valintanauhan alapuolella"; - this.ribbonContextMenuAddItem = "Lisää pikatyökaluriville"; - this.ribbonContextMenuAddGroup = "Lisää ryhmä pikatyökaluriviin"; - this.ribbonContextMenuAddGallery = "Lisää valikoima pikatyökaluriviin"; - this.ribbonContextMenuAddMenu = "Lisää valikko pikatyökaluriviin"; - this.ribbonContextMenuRemoveItem = "Poista pikatyökaluriviltä"; - this.ribbonContextMenuCustomizeQuickAccessToolbar = "Mukauta pikatyökaluriviä..."; - this.ribbonContextMenuShowBelow = "Näytä pikatyökalurivi valintanauhan alapuolella"; - this.ribbonContextMenuShowAbove = "Näytä pikatyökalurivi valintanauhan yläpuolella"; - this.ribbonContextMenuCustomizeRibbon = "Mukauta valintanauhaa..."; - this.ribbonContextMenuMinimizeRibbon = "Pienennä valintanauha"; - this.screenTipDisableReasonHeader = "Tämä komento on tällä hetkellä poissa käytöstä"; - } - - #endregion - - #region Norwegian - - private void LoadNorwegian() - { - // Backstage button text & key tip - this.backstageButtonText = "Fil"; - this.backstageButtonKeyTip = "F"; - - // See right-top corner... (two different tooltips must be if you press it) - this.minimizeButtonScreenTipTitle = "Minimer båndet (Ctrl + F1)"; - this.minimizeButtonScreenTipText = "Viser bare kategorinavnene på båndet"; - this.expandButtonScreenTipTitle = "Utvider båndet (Ctrl + F1)"; - this.expandButtonScreenTipText = "Vis båndet slik at det alltid er utvidet selv etter at du har valgt en kommando"; - - // QAT tooltips and menu items - this.quickAccessToolBarDropDownButtonTooltip = "Tilpass verktøylinje for hurtigtilgang"; - this.quickAccessToolBarMoreControlsButtonTooltip = "Flere kontroller"; // When two arrows appear ">>" - this.quickAccessToolBarMenuHeader = "Tilpass verktøylinje for hurtigtilgang"; - this.quickAccessToolBarMenuShowAbove = "Vis over båndet"; - this.quickAccessToolBarMenuShowBelow = "Vis under båndet"; - - // Click on Ribbon to show context menu - this.ribbonContextMenuAddItem = "Legg til på verktøylinje for hurtigtilgang"; // Button - this.ribbonContextMenuAddGroup = "Legg til gruppe på verktøylinje for hurtigtilgang"; // For ex., by collapsed group - this.ribbonContextMenuAddGallery = "Legg til galleri på verktøylinje for hurtigtilgang"; // For ex., by opened font context menu - this.ribbonContextMenuAddMenu = "Legg til meny på verktøylinje for hurtigtilgang"; // By dashed splitter in context menu - this.ribbonContextMenuRemoveItem = "Fjern verktøylinjen for hurtigtilgang"; // By item in QAT - this.ribbonContextMenuCustomizeQuickAccessToolbar = "Tilpass verktøylinje for hurtigtilgang..."; - this.ribbonContextMenuShowBelow = "Vis verktøylinjen for hurtigtilgang under båndet"; - this.ribbonContextMenuShowAbove = "Vis verktøylinjen for hurtigtilgang over båndet"; - this.ribbonContextMenuCustomizeRibbon = "Tilpass båndet..."; - this.ribbonContextMenuMinimizeRibbon = "Minimer båndet"; - - // To see it in Word: open *.doc (not *.docx) and see Insert->Screenshot - // (This prop was introduced after v1.3) - this.screenTipDisableReasonHeader = "Denne kommandoen er for øyeblikket deaktivert."; - } - - #endregion - - #region Turkish - - private void LoadTurkish() - { - // Backstage button text & key tip - this.backstageButtonText = "Dosya"; - this.backstageButtonKeyTip = "D"; - - // See right-top corner... (two different tooltips must be if you press it) - this.minimizeButtonScreenTipTitle = "Şeridi Daralt (Ctrl+F1)"; - this.minimizeButtonScreenTipText = "Daha fazla alana mı\nihtiyacınız var? Şeridi daraltın, yalnızca sekme isimleri görünsün."; - this.expandButtonScreenTipTitle = "Şeridi Sabitle (Ctrl+F1)"; - this.expandButtonScreenTipText = "Şeridi görmek mi istiyorsunuz? Çalışırken açık tutun."; - - // QAT tooltips and menu items - this.quickAccessToolBarDropDownButtonTooltip = "Hızlı Erişim Araç Çubuğu'nu Özelleştir"; - this.quickAccessToolBarMoreControlsButtonTooltip = "Diğer denetimler"; // When two arrows appear ">>" - this.quickAccessToolBarMenuHeader = "Hızlı Erişim Araç Çubuğu'nu Özelleştir"; - this.quickAccessToolBarMenuShowAbove = "Şeridin Üstünde Göster"; - this.quickAccessToolBarMenuShowBelow = "Şeridin Altında Göster"; - - // Click on Ribbon to show context menu - this.ribbonContextMenuAddItem = "Hızlı Erişim Araç Çubuğu'na Ekle"; // Button - this.ribbonContextMenuAddGroup = "Grubu Hızlı Erişim Araç Çubuğu'na Ekle"; // For ex., by collapsed group - this.ribbonContextMenuAddGallery = "Galeriyi Hızlı Erişim Araç Çubuğu'na Ekle"; // For ex., by opened font context menu - this.ribbonContextMenuAddMenu = "Menüyü Hızlı Erişim Araç Çubuğu'na Ekle"; // By dashed splitter in context menu - this.ribbonContextMenuRemoveItem = "Hızlı Erişim Araç Çubuğu'ndan Kaldır"; // By item in QAT - this.ribbonContextMenuCustomizeQuickAccessToolbar = "Hızlı Erişim Araç Çubuğu'nu Özelleştir"; - this.ribbonContextMenuShowBelow = "Hızlı Erişim Araç Çubuğu'nu Şeridin Altında Göster"; - this.ribbonContextMenuShowAbove = "Hızlı Erişim Araç Çubuğu'nu Şeridin Üstünde Göster"; - this.ribbonContextMenuCustomizeRibbon = "Şeridi Özelleştir..."; - this.ribbonContextMenuMinimizeRibbon = "Şeridi Daralt"; - - // To see it in Word: open *.doc (not *.docx) and see Insert->Screenshot - // (This prop was introduced after v1.3) - this.screenTipDisableReasonHeader = "Bu komut şu anda devre dışı"; - this.screenTipF1LabelHeader = "Yardım için F1'e basın."; - - // Right-click on status bar to see it. NEW! from v2.0 - this.customizeStatusBar = "Durum Çubuğunu Özelleştir"; - } - - #endregion - - #region Hebrew - - private void LoadHebrew() - { - // Backstage button text & key tip - this.backstageButtonText = "קובץ"; - this.backstageButtonKeyTip = "ק"; - - // See right-top corner... (two different tooltips must be if you press it) - this.minimizeButtonScreenTipTitle = "מזער את רצועת הכלים (Ctrl + F1)"; - this.minimizeButtonScreenTipText = "הצג רק את שמות הכרטיסיות\nברצועת הכלים."; - this.expandButtonScreenTipTitle = "הרחב את רצועת הכלים (Ctrl + F1)"; - this.expandButtonScreenTipText = "הצג את רצועת הכלים כשהיא\nמורחבת תמיד, גם לאחר\nשתלחץ על הפקודה."; - - // QAT tooltips and menu items - this.quickAccessToolBarDropDownButtonTooltip = "התאמה אישית של סרגל הכלים לגישה מהירה"; - this.quickAccessToolBarMoreControlsButtonTooltip = "פקודות נוספות"; // When two arrows appear ">>" - this.quickAccessToolBarMenuHeader = "התאמה אישית של סרגל הכלים לגישה מהירה"; - this.quickAccessToolBarMenuShowAbove = "הצג מעל לרצועת הכלים"; - this.quickAccessToolBarMenuShowBelow = "הצג מעל לרצועת הכלים"; - - // Click on Ribbon to show context menu - this.ribbonContextMenuAddItem = "הוסף לסרגל הכלים לגישה מהירה"; // Button - this.ribbonContextMenuAddGroup = "הוסף קבוצה לסרגל הכלים לגישה מהירה"; // For ex., by collapsed group - this.ribbonContextMenuAddGallery = "הוסף גלריה לסרגל הכלים לגישה מהירה"; // For ex., by opened font context menu - this.ribbonContextMenuAddMenu = "הוסף תפריט לסרגל הכלים לגישה מהירה"; // By dashed splitter in context menu - this.ribbonContextMenuRemoveItem = "הסר מסרגל הכלים לגישה מהירה"; // By item in QAT - this.ribbonContextMenuCustomizeQuickAccessToolbar = "של סרגל הכלים רצועת הכלים..."; - this.ribbonContextMenuShowBelow = "הצג את סרגל הכלים לגישה מהירה מתחת לרצועת הכלים"; - this.ribbonContextMenuShowAbove = "הצג את סרגל הכלים לגישה מהירה מעל לרצועת הכלים"; - this.ribbonContextMenuCustomizeRibbon = "התאמה אישית של רצועת הכלים..."; - this.ribbonContextMenuMinimizeRibbon = "מזער את רצועת הכלים"; - - // To see it in Word: open *.doc (not *.docx) and see Insert->Screenshot - // (This prop was introduced after v1.3) - this.screenTipDisableReasonHeader = "פקודה זו אינה זמינה כעת."; - - // Right-click on status bar to see it. NEW! from v2.0 - this.customizeStatusBar = "התאמה אישית של שורת המצב"; - } - - #endregion - - #region Greek - - private void LoadGreek() - { - // Backstage button text & key tip - this.backstageButtonText = "Αρχείο"; - this.backstageButtonKeyTip = "Α"; - - // See right-top corner... (two different tooltips must be if you press it) - this.minimizeButtonScreenTipTitle = "Ελαχιστοποίηση της Κορδέλας (Ctrl + F1)"; - this.minimizeButtonScreenTipText = "Εμφάνιση μόνο των ονομάτων καρτελών στην Κορδέλα."; - this.expandButtonScreenTipTitle = "Ανάπτυξη της Κορδέλας (Ctrl + F1)"; - this.expandButtonScreenTipText = "Εμφάνιση της Κορδέλας προκειμένου να αναπτύσσεται πάντα, ακόμα και αφού κάνετε κλικ σε μια εντολή."; - - // QAT tooltips and menu items - this.quickAccessToolBarDropDownButtonTooltip = "Προσαρμογή γραμμής εργαλείων γρήγορης πρόσβασης"; - this.quickAccessToolBarMoreControlsButtonTooltip = "Περισσότερες εντολές"; // When two arrows appear ">>" - this.quickAccessToolBarMenuHeader = "Προσαρμογή γραμμής εργαλείων γρήγορης πρόσβασης"; - this.quickAccessToolBarMenuShowAbove = "Εμφάνιση πάνω από την Κορδέλα"; - this.quickAccessToolBarMenuShowBelow = "Εμφάνιση κάτω από την Κορδέλα"; - - // Click on Ribbon to show context menu - this.ribbonContextMenuAddItem = "Προσθήκη στη γραμμή εργαλείων γρήγορης πρόσβασης"; // Button - this.ribbonContextMenuAddGroup = "Προσθήκη ομάδας στη γραμμή εργαλείων γρήγορης πρόσβασης"; // For ex., by collapsed group - this.ribbonContextMenuAddGallery = "Προσθήκη συλλογής στη γραμμή εργαλείων γρήγορης πρόσβασης"; // For ex., by opened font context menu - this.ribbonContextMenuAddMenu = "Προσθήκη μενού στη γραμμή εργαλείων γρήγορης πρόσβασης"; // By dashed splitter in context menu - this.ribbonContextMenuRemoveItem = "Κατάργηση από τη γραμμή εργαλείων γρήγορης πρόσβασης"; // By item in QAT - this.ribbonContextMenuCustomizeQuickAccessToolbar = "Προσαρμογή γραμμής εργαλείων γρήγορης πρόσβασης..."; - this.ribbonContextMenuShowBelow = "Εμφάνιση της γραμμής εργαλείων γρήγορης πρόσβασης κάτω από την Κορδέλα"; - this.ribbonContextMenuShowAbove = "Εμφάνιση της γραμμής εργαλείων γρήγορης πρόσβασης πάνω από την Κορδέλα"; - this.ribbonContextMenuCustomizeRibbon = "Προσαρμογή της Κορδέλας..."; - this.ribbonContextMenuMinimizeRibbon = "Ελαχιστοποίηση της Κορδέλας"; - - // To see it in Word: open *.doc (not *.docx) and see Insert->Screenshot - // (This prop was introduced after v1.3) - this.screenTipDisableReasonHeader = "Αυτή η εντολή είναι απενεργοποιημένη προς το παρόν."; - - // Right-click on status bar to see it. NEW! from v2.0 - this.customizeStatusBar = "Προσαρμογή γραμμής κατάστασης"; - } - - #endregion - - #region Korean - - private void LoadKorean() - { - // Backstage button text & key tip - this.backstageButtonText = "파일"; - this.backstageButtonKeyTip = "F"; - - // See right-top corner... (two different tooltips must be if you press it) - this.minimizeButtonScreenTipTitle = "리본 메뉴를 최소화 합니다 (Ctrl + F1)"; - this.minimizeButtonScreenTipText = "리본 메뉴를 표시하거나 숨깁니다\n\n리본 메뉴가 숨김 상태일때만,\n탭이름이 보여집니다"; - this.expandButtonScreenTipTitle = "리본 메뉴를 표시합니다 (Ctrl + F1)"; - this.expandButtonScreenTipText = "리본 메뉴를 표시하거나 숨깁니다\n\n리본 메뉴가 숨김 상태일때만,\n탭이름이 보여집니다"; - - // QAT tooltips and menu items - this.quickAccessToolBarDropDownButtonTooltip = "빠른 실행 도구 모음 사용자 지정"; - this.quickAccessToolBarMoreControlsButtonTooltip = "기타 컨트롤들"; // When two arrows appear ">>" - this.quickAccessToolBarMenuHeader = "빠른 실행 도구 모음 사용자 지정"; - this.quickAccessToolBarMenuShowAbove = "리본 메뉴 위에 표시"; - this.quickAccessToolBarMenuShowBelow = "리본 메뉴 아래에 표시"; - - // Click on Ribbon to show context menu - this.ribbonContextMenuAddItem = "빠른 실행 도구 모음에 추가"; // Button - this.ribbonContextMenuAddGroup = "그룹을 빠른 실행 도구 모음에 추가"; // For ex., by collapsed group - this.ribbonContextMenuAddGallery = "갤러리를 빠른 실행 도구 모음에 추가"; // For ex., by opened font context menu - this.ribbonContextMenuAddMenu = "메뉴를 빠른 실행 도구 모음에 추가"; // By dashed splitter in context menu - this.ribbonContextMenuRemoveItem = "빠른 실행 도구 모음에서 단추 제거"; // By item in QAT - this.ribbonContextMenuCustomizeQuickAccessToolbar = "빠른 실행 도구 모음 사용자 지정..."; - this.ribbonContextMenuShowBelow = "리본 메뉴 아래에 빠른 실행 도구 모음 표시"; - this.ribbonContextMenuShowAbove = "리본 메뉴 위에 빠른 실행 도구 모음 표시"; - this.ribbonContextMenuCustomizeRibbon = "리본 메뉴 사용자 지정..."; - this.ribbonContextMenuMinimizeRibbon = "리본 메뉴 최소화"; - - // To see it in Word: open *.doc (not *.docx) and see Insert->Screenshot - // (This prop was introduced after v1.3) - this.screenTipDisableReasonHeader = "이 명령은 현재 사용할 수 없습니다."; - - // Right-click on status bar to see it. NEW! from v2.0 - this.customizeStatusBar = "상태 표시줄 사용자 지정"; - } - - #endregion - - #region Lithuanian - - private void LoadLithuanian() - { - // Backstage button text & key tip - this.backstageButtonText = "Failas"; - this.backstageButtonKeyTip = "F"; - - // See right-top corner... (two different tooltips must be if you press it) - this.minimizeButtonScreenTipTitle = "Minimizuoti juostelę (Ctrl + F1)"; - this.minimizeButtonScreenTipText = "Juostelėje rodyti tik skirtukų pavadinimus."; - this.expandButtonScreenTipTitle = "Išplėsti juostelę (Ctrl + F1)"; - this.expandButtonScreenTipText = "Rodyti juostelę taip, kad visada butų išskleista net ir spustelėjus komandą."; - - // QAT tooltips and menu items - - this.quickAccessToolBarDropDownButtonTooltip = "Tinkinti sparčiosios prieigos įrankių juostą"; - this.quickAccessToolBarMoreControlsButtonTooltip = "Daugiau valdiklių"; // When two arrows appear ">>" - this.quickAccessToolBarMenuHeader = "Tinkinti sparčiosios prieigos įrankių juostą"; - this.quickAccessToolBarMenuShowAbove = "Rodyti virš juostelės"; - this.quickAccessToolBarMenuShowBelow = "Rodyti po juostele"; - - // Click on Ribbon to show context menu - this.ribbonContextMenuAddItem = "Pridėti į sparčiosios prieigos įrankių juostą"; // Button - this.ribbonContextMenuAddGroup = "Pridėti į sparčiosios prieigos įrankių juostą"; - this.ribbonContextMenuAddGallery = "Įtraukti galeriją į sparčiosios prieigos įrankių juostą"; // For ex., by opened font context menu - this.ribbonContextMenuAddMenu = "Pridėti į sparčiosios prieigos įrankių juostą"; - this.ribbonContextMenuRemoveItem = "Šalinti iš sparčiosios prieigos įrankių juostos"; // By item in QAT - this.ribbonContextMenuCustomizeQuickAccessToolbar = "Tinkinti sparčiosios prieigos įrankių juostą..."; - this.ribbonContextMenuShowBelow = "Rodyti po juostele"; - this.ribbonContextMenuShowAbove = "Rodyti virš juostelės"; - this.ribbonContextMenuCustomizeRibbon = "Tinkinti juostelę:"; - this.ribbonContextMenuMinimizeRibbon = "Minimizuoti juostelę"; - } - - #endregion - - #region Vietnamese - - private void LoadVietnamese() - { - // Backstage button text & key tip - this.backstageButtonText = "Tệp"; - this.backstageButtonKeyTip = "F"; - - // See right-top corner... (two different tooltips must be if you press it) - this.minimizeButtonScreenTipTitle = "Thu gọn Ruy băng (Ctrl + F1)"; - this.minimizeButtonScreenTipText = "Hiện hoặc ẩn Ruy băng\n\nKhi Ruy băng ẩn, chỉ có tên thẻ được hiện"; - this.expandButtonScreenTipTitle = "Mở rộng Ruy băng (Ctrl + F1)"; - this.expandButtonScreenTipText = "Hiện hoặc ẩn Ruy băng\n\nKhi Ruy băng ẩn, chỉ có tên thẻ được hiện"; - - // QAT tooltips and menu items - this.quickAccessToolBarDropDownButtonTooltip = "Tùy chỉnh thanh công cụ Truy cập nhanh"; - this.quickAccessToolBarMoreControlsButtonTooltip = "Thêm điều khiển"; // khi có hai mũi tên ">>" - this.quickAccessToolBarMenuHeader = "Tùy chỉnh thanh công cụ Truy cập nhanh"; - this.quickAccessToolBarMenuShowAbove = "Hiện trên thanh Ruy băng"; - this.quickAccessToolBarMenuShowBelow = "Hiện dưới thanh Ruy băng"; - - // Click on Ribbon to show context menu - this.ribbonContextMenuAddItem = "Thêm vào thanh công cụ Truy cập nhanh"; // Button - this.ribbonContextMenuAddGroup = "Thêm nhóm vào thanh công cụ Truy cập nhanh"; // For ex., by collapsed group - this.ribbonContextMenuAddGallery = "Thêm bộ sưu tập vào thanh công cụ Truy cập nhanh"; // For ex., by opened font context menu - this.ribbonContextMenuAddMenu = "Thêm menu vào thanh công cụ Truy cập nhanh"; // By dashed splitter in context menu - this.ribbonContextMenuRemoveItem = "Loại"; // By item in QAT - this.ribbonContextMenuCustomizeQuickAccessToolbar = "Tùy chỉnh thanh công cụ Truy cập nhanh..."; - this.ribbonContextMenuShowBelow = "Hiện thanh công cụ truy cập nhanh dưới thanh Ruy băng"; - this.ribbonContextMenuShowAbove = "Hiện thanh công cụ truy cập nhanh trên thanh Ruy băng"; - this.ribbonContextMenuCustomizeRibbon = "Tùy biến thanh Ruy băng..."; - this.ribbonContextMenuMinimizeRibbon = "Thu gọn Ruy băng"; - - // To see it in Word: open *.doc (not *.docx) and see Insert->Screenshot - // (This prop was introduced after v1.3) - this.screenTipDisableReasonHeader = "Lệnh này hiện bị tắt."; - - // Right-click on status bar to see it. NEW! from v2.0 - this.customizeStatusBar = "Tùy biến thanh Trạng thái"; - } - - #endregion - - #region Sinhala (Sri Lanka) - - private void LoadSinhala() - { - // Backstage button text & key tip - this.backstageButtonText = "ගොනුව"; - this.backstageButtonKeyTip = "න1"; - - // See right-top corner... (two different tooltips must be if you press it) - this.minimizeButtonScreenTipTitle = "රිබනය හකුළන්න (Ctrl + F1)"; - this.minimizeButtonScreenTipText = "රිබනය මත පටිති නාම පමණක් පෙන්වන්න."; - this.expandButtonScreenTipTitle = "රිබනය විහිදන්න (Ctrl + F1)"; - this.expandButtonScreenTipText = "රිබනය පෙන්වන්න, එවිට ඔබ\n\n විධානයක් ක්ලික් කළද එය\n\n සැමවිටම විහිදී පවතී."; - - // QAT tooltips and menu items - this.quickAccessToolBarDropDownButtonTooltip = "ඉක්මන් ප්‍රෙව්ශ මෙවලම් තීරය අභිමත කරණය"; - this.quickAccessToolBarMoreControlsButtonTooltip = "තවත් විධාන"; // When two arrows appear ">>" - this.quickAccessToolBarMenuHeader = "ඉක්මන් ප්‍රෙව්ශ මෙවලම් තීරය අභිමත කරණය"; - this.quickAccessToolBarMenuShowAbove = "රිබනයට ඉහලින් පෙන්වන්න"; - this.quickAccessToolBarMenuShowBelow = "රිබනයට පහලින් පෙන්වන්න"; - - // Click on Ribbon to show context menu - this.ribbonContextMenuAddItem = "ඉක්මන් ප්‍රෙව්ශ මෙවලම් තීරයට එක් කරන්න"; // Button - this.ribbonContextMenuAddGroup = "ඉක්මන් ප්‍රෙව්ශ මෙවලම් තීරයට එක් කරන්න"; // For ex., by collapsed group - this.ribbonContextMenuAddGallery = "ඉක්මන් ප්‍රෙව්ශ මෙවලම් තීරයට ගැලරිය එක් කරන්න"; // For ex., by opened font context menu - this.ribbonContextMenuAddMenu = "ඉක්මන් ප්‍රෙව්ශ මෙවලම් තීරයට මෙනුව එක් කරන්න"; // By dashed splitter in context menu - this.ribbonContextMenuRemoveItem = "ඉක්මන් ප්‍රෙව්ශ මෙවලම් තීරයෙන් ඉවත් කරන්න"; // By item in QAT - this.ribbonContextMenuCustomizeQuickAccessToolbar = "ඉක්මන් ප්‍රෙව්ශ මෙවලම් තීරය අභිමත කරණය කරන්න..."; - this.ribbonContextMenuShowBelow = "ඉක්මන් ප්‍රෙව්ශ මෙවලම් තීරය රිබනයට පහලින් පෙන්වන්න"; - this.ribbonContextMenuShowAbove = "ඉක්මන් ප්‍රෙව්ශ මෙවලම් තීරය රිබනයට ඉහලින් පෙන්වන්න"; - this.ribbonContextMenuCustomizeRibbon = "රිබනය අභිමත කරණය කරන්න..."; - this.ribbonContextMenuMinimizeRibbon = "රිබනය හකුළන්න"; - - // To see it in Word: open *.doc (not *.docx) and see Insert->Screenshot - // (This prop was introduced after v1.3) - this.screenTipDisableReasonHeader = "මෙම විධානය දැනට භාවිතා කළ නොහැක"; - - // Right-click on status bar to see it. NEW! from v2.0 - this.customizeStatusBar = "තත්ව තීරුව අභිමත කරණය"; - } - - #endregion - - #region Slovenian - - private void LoadSlovenian() - { - // Backstage button text & key tip - this.backstageButtonText = "Datoteka"; - this.backstageButtonKeyTip = "D"; - - // See right-top corner... (two different tooltips must be if you press it) - this.minimizeButtonScreenTipTitle = "Minimiraj trak (Ctrl + F1)"; - this.minimizeButtonScreenTipText = "Pokaži ali skrij trak\n\nKo je trak skrit, so prikazani samo zavihki"; - this.expandButtonScreenTipTitle = "Razširi trak (Ctrl + F1)"; - this.expandButtonScreenTipText = "Pokaži ali skrij trak\n\nKo je trak skrit, so prikazani samo zavihki"; - - // QAT tooltips and menu items - this.quickAccessToolBarDropDownButtonTooltip = "Prilagodi orodno vrstico za hitri dostop"; - this.quickAccessToolBarMoreControlsButtonTooltip = "Več ukazov"; // When two arrows appear ">>" - this.quickAccessToolBarMenuHeader = "Prilagodi orodno vrstico za hitri dostop"; - this.quickAccessToolBarMenuShowAbove = "Pokaži nad trakom"; - this.quickAccessToolBarMenuShowBelow = "Pokaži pod trakom"; - - // Click on Ribbon to show context menu - this.ribbonContextMenuAddItem = "Dodaj v orodno vrstico za hitri dostop"; // Button - this.ribbonContextMenuAddGroup = "Dodaj skupino orodni vrstici za hitri dostop"; // For ex., by collapsed group - this.ribbonContextMenuAddGallery = "Dodaj galerijo orodni vrstici za hitri dostop"; // For ex., by opened font context menu - this.ribbonContextMenuAddMenu = "Dodaj meni orodni vrstici za hitri dostop"; // By dashed splitter in context menu - this.ribbonContextMenuRemoveItem = "Odstrani iz orodne vrstice za hitri dostop"; // By item in QAT - this.ribbonContextMenuCustomizeQuickAccessToolbar = "Prilagodi orodno vrstico za hitri dostop..."; - this.ribbonContextMenuShowBelow = "Pokaži orodno vrstico za hitri dostop pod trakom"; - this.ribbonContextMenuShowAbove = "Pokaži orodno vrstico za hitri dostop nad trakom"; - this.ribbonContextMenuCustomizeRibbon = "Prilagodi trak..."; - this.ribbonContextMenuMinimizeRibbon = "Minimiraj trak"; - - // To see it in Word: open *.doc (not *.docx) and see Insert->Screenshot - // (This prop was introduced after v1.3) - this.screenTipDisableReasonHeader = "Ta ukaz je trenutno onemogočen."; - - // Right-click on status bar to see it. NEW! from v2.0 - this.customizeStatusBar = "Prilagodi vrstico stanja"; - } - - #endregion - - #region Catalan - - private void LoadCatalan() - { - // Backstage button text & key tip - this.backstageButtonText = "Fitxer"; - this.backstageButtonKeyTip = "F"; - - // See right-top corner... (two different tooltips must be if you press it) - // TRANSLATOR'S NOTE: This block is not shown at Windows 7's Apps (WordPad or Paint) - this.minimizeButtonScreenTipTitle = "Minimitza la cinta (Ctrl + F1)"; - this.minimizeButtonScreenTipText = "Ensenya o amaga la cinta\n\nQuan la cinta no es mostri, només s'ensenyen els noms de les pestanyes"; - this.expandButtonScreenTipTitle = "Expandeix la cinta (Ctrl + F1)"; - this.expandButtonScreenTipText = "Ensenya o amaga la cinta\n\nQuan la cinta no es mostri, només s'ensenyen els noms de les pestanyes"; - - // QAT tooltips and menu items - this.quickAccessToolBarDropDownButtonTooltip = "Personalitza la barra d'eines d'accés ràpid"; - this.quickAccessToolBarMoreControlsButtonTooltip = "Més controls"; // When two arrows appear ">>" - this.quickAccessToolBarMenuHeader = "Personalitza la barra d'eines d'accés ràpid"; - this.quickAccessToolBarMenuShowAbove = "Mostra sobre la cinta"; - this.quickAccessToolBarMenuShowBelow = "Mostra sota la cinta"; - - // Click on Ribbon to show context menu - this.ribbonContextMenuAddItem = "Afegeix a la barra d'eines d'accés ràpid"; // Button - this.ribbonContextMenuAddGroup = "Afegeix grup a la barra d'eines d'accés ràpid"; // For ex., by collapsed group - this.ribbonContextMenuAddGallery = "Afegeix galeria a la barra d'eines d'accés ràpid"; // For ex., by opened font context menu - this.ribbonContextMenuAddMenu = "Afegeix menú a la barra d'eines d'accés ràpid"; // By dashed splitter in context menu - this.ribbonContextMenuRemoveItem = "Elimina la barra d'eines d'accés ràpid"; // By item in QAT - this.ribbonContextMenuCustomizeQuickAccessToolbar = "Personalitza la barra d'eines d'accés ràpid..."; - this.ribbonContextMenuShowBelow = "Mostra la barra d'eines d'accés ràpid sota la cinta"; - this.ribbonContextMenuShowAbove = "Mostra la barra d'eines d'accés ràpid sobre la cinta"; - this.ribbonContextMenuCustomizeRibbon = "Personalitza la cinta..."; - this.ribbonContextMenuMinimizeRibbon = "Minimitza la cinta"; - } - - #endregion - - #region Estonian - - private void LoadEstonian() - { - // Backstage button text & key tip - this.backstageButtonText = "Fail"; - this.backstageButtonKeyTip = "F"; - - // See right-top corner... (two different tooltips must be if you press it) - this.minimizeButtonScreenTipTitle = "Ahenda menüülint (Ctrl+F1)"; - this.minimizeButtonScreenTipText = "Kas vajate rohkem ruumi? Ahendage lint, siis kuvatakse \nainult menüünimed."; - this.expandButtonScreenTipTitle = "Kinnita lint (Ctrl+F1)"; - this.expandButtonScreenTipText = "Kas soovite, et lint oleks kuvatud? Saate selle töötamise \najal avatuna hoida."; - - // QAT tooltips and menu items - this.quickAccessToolBarDropDownButtonTooltip = "Kohanda kiirpääsuriba"; - this.quickAccessToolBarMoreControlsButtonTooltip = "Rohkem juhtelemente"; // When two arrows appear ">>" - this.quickAccessToolBarMenuHeader = "Kohanda kiirpääsuriba"; - this.quickAccessToolBarMenuShowAbove = "Kuva lindi kohal"; - this.quickAccessToolBarMenuShowBelow = "Kuva lindi all"; - - // Click on Ribbon to show context menu - this.ribbonContextMenuAddItem = "Lisa kiirpääsuribale"; // Button - this.ribbonContextMenuAddGroup = "Lisa rühm kiirpääsuribale"; // For ex., by collapsed group - this.ribbonContextMenuAddGallery = "Lisa galerii kiirpääsuribale"; // For ex., by opened font context menu - this.ribbonContextMenuAddMenu = "Lisa menüü kiirpääsuribale"; // By dashed splitter in context menu - this.ribbonContextMenuRemoveItem = "Eemalda kiirpääsuribalt"; // By item in QAT - this.ribbonContextMenuCustomizeQuickAccessToolbar = "Kohanda kiirpääsuriba..."; - this.ribbonContextMenuShowBelow = "Kuva kiirpääsuriba lindi all"; - this.ribbonContextMenuShowAbove = "Kuva kiirpääsuriba lindi kohal"; - this.ribbonContextMenuCustomizeRibbon = "Kohanda linti..."; - this.ribbonContextMenuMinimizeRibbon = "Ahenda menüülint"; - - // To see it in Word: open *.doc (not *.docx) and see Insert->Screenshot - // (This prop was introduced after v1.3) - //Screentips - this.screenTipDisableReasonHeader = "See käsk on praegu keelatud."; - this.screenTipF1LabelHeader = "Spikri kuvamiseks vajutage klahvi F1"; - - // Right-click on status bar to see it. NEW! from v2.0 - this.customizeStatusBar = "Kohanda olekuriba"; - } - - #endregion - - //Add by gamegear.tw - //Note: Adding Tradchinese - #region TradChinese - private void LoadTradChinese() - { - // Backstage button text & key tip - this.backstageButtonText = "檔案"; - this.backstageButtonKeyTip = "F"; - - // See right-top corner... (two different tooltips must be if you press it) - this.minimizeButtonScreenTipTitle = "功能區最小化 (Ctrl + F1)"; - this.minimizeButtonScreenTipText = "僅顯示功能區上的選項名稱,點擊選項後可顯示命令。"; - this.expandButtonScreenTipTitle = "展開功能區 (Ctrl + F1)"; - this.expandButtonScreenTipText = "始終顯示功能區選項及命令。"; - - // QAT tooltips and menu items - this.quickAccessToolBarDropDownButtonTooltip = "自訂快速存取工具列"; - this.quickAccessToolBarMoreControlsButtonTooltip = "其他命令"; // When two arrows appear ">>" - this.quickAccessToolBarMenuHeader = "自訂快速存取工具列"; - this.quickAccessToolBarMenuShowAbove = "在功能區上方顯示"; - this.quickAccessToolBarMenuShowBelow = "在功能區下方顯示"; - - // Click on Ribbon to show context menu - this.ribbonContextMenuAddItem = "新增到快速存取工具列"; // Button - this.ribbonContextMenuAddGroup = "將群組新增到快速存取工具列"; // For ex., by collapsed group - this.ribbonContextMenuAddGallery = "將樣式新增到快速存取工具列"; // For ex., by opened font context menu - this.ribbonContextMenuAddMenu = "將選單新增到快速存取工具列"; // By dashed splitter in context menu - this.ribbonContextMenuRemoveItem = "將選單從快速存取工具列中移除"; // By item in QAT - this.ribbonContextMenuCustomizeQuickAccessToolbar = "自訂快速存取工具列..."; - this.ribbonContextMenuShowBelow = "在功能表下方顯示快速存取工具列"; - this.ribbonContextMenuShowAbove = "在功能表上方顯示快速存取工具列"; - this.ribbonContextMenuCustomizeRibbon = "自訂功能區..."; - this.ribbonContextMenuMinimizeRibbon = "功能區最小化"; - - // To see it in Word: open *.doc (not *.docx) and see Insert->Screenshot - // (This prop was introduced after v1.3) - this.screenTipDisableReasonHeader = "命令已被禁用。"; - - // Right-click on status bar to see it. NEW! from v2.0 - this.customizeStatusBar = "自訂狀態工具列"; - } - #endregion - - - } -} +using System.ComponentModel; +using System.Globalization; + +namespace Fluent +{ + /// + /// Contains localizable Ribbon's properties. + /// Set Culture property to change current Ribbon localization or + /// set properties independently to use your localization + /// + public class RibbonLocalization : INotifyPropertyChanged + { + #region Implementation of INotifyPropertyChanged + + /// + /// Occurs then property is changed + /// + public event PropertyChangedEventHandler PropertyChanged; + + // Raises PropertYChanegd event + private void RaisePropertyChanged(string propertyName) + { + if (this.PropertyChanged != null) + this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); + } + + #endregion + + #region Culture + + private CultureInfo culture; + + /// + /// Gets or sets current culture used for localization + /// + public CultureInfo Culture + { + get { return this.culture; } + set + { + if (!Equals(this.culture, value)) + { + this.culture = value; + this.LoadCulture(this.culture); + this.RaisePropertyChanged("Culture"); + } + } + } + + #endregion + + #region Text of backstage button + + // Text of backstage button + private string backstageButtonText; + + /// + /// Gets or sets text of backstage button + /// + public string BackstageButtonText + { + get { return this.backstageButtonText; } + set + { + if (this.backstageButtonText != value) + { + this.backstageButtonText = value; + this.RaisePropertyChanged("BackstageButtonText"); + } + } + } + + #endregion + + #region KeyTip of backstage button + + // KeyTip of backstage button + private string backstageButtonKeyTip; + + /// + /// Gets or sets KeyTip of backstage button + /// + public string BackstageButtonKeyTip + { + get { return this.backstageButtonKeyTip; } + set + { + if (this.backstageButtonKeyTip != value) + { + this.backstageButtonKeyTip = value; + this.RaisePropertyChanged("BackstageButtonKeyTip"); + } + } + } + + #endregion + + #region Minimize Button ScreenTip Title + + // Minimize Button ScreenTip Title + private string minimizeButtonScreenTipTitle; + + /// + /// Minimize Button ScreenTip Title + /// + public string MinimizeButtonScreenTipTitle + { + get { return this.minimizeButtonScreenTipTitle; } + set + { + if (this.minimizeButtonScreenTipTitle != value) + { + this.minimizeButtonScreenTipTitle = value; + this.RaisePropertyChanged("MinimizeButtonScreenTipTitle"); + } + } + } + + #endregion + + #region Minimize Button ScreenTip Text + + // Minimize Button ScreenTip Text + private string minimizeButtonScreenTipText; + + /// + /// Minimize Button ScreenTip Text + /// + public string MinimizeButtonScreenTipText + { + get { return this.minimizeButtonScreenTipText; } + set + { + if (this.minimizeButtonScreenTipText != value) + { + this.minimizeButtonScreenTipText = value; + this.RaisePropertyChanged("MinimizeButtonScreenTipText"); + } + } + } + + #endregion + + #region Expand Button ScreenTip Title + + // Expand Button ScreenTip Title + private string expandButtonScreenTipTitle; + + /// + /// Expand Button ScreenTip Title + /// + public string ExpandButtonScreenTipTitle + { + get { return this.expandButtonScreenTipTitle; } + set + { + if (this.expandButtonScreenTipTitle != value) + { + this.expandButtonScreenTipTitle = value; + this.RaisePropertyChanged("ExpandButtonScreenTipTitle"); + } + } + } + + #endregion + + #region Expand Button ScreenTip Text + + // Expand Button ScreenTip Text + private string expandButtonScreenTipText; + + /// + /// Expand Button ScreenTip Text + /// + public string ExpandButtonScreenTipText + { + get { return this.expandButtonScreenTipText; } + set + { + if (this.expandButtonScreenTipText != value) + { + this.expandButtonScreenTipText = value; + this.RaisePropertyChanged("ExpandButtonScreenTipText"); + } + } + } + + #endregion + + #region Quick Access ToolBar DropDown Button ToolTip + + // Quick Access ToolBar DropDown Button ToolTip + private string quickAccessToolBarDropDownButtonTooltip; + + /// + /// Quick Access ToolBar DropDown Button ToolTip + /// + public string QuickAccessToolBarDropDownButtonTooltip + { + get { return this.quickAccessToolBarDropDownButtonTooltip; } + set + { + if (this.quickAccessToolBarDropDownButtonTooltip != value) + { + this.quickAccessToolBarDropDownButtonTooltip = value; + this.RaisePropertyChanged("QuickAccessToolBarDropDownButtonTooltip"); + } + } + } + + #endregion + + #region Quick Access ToolBar MoreControls Button ToolTip + + // Quick Access ToolBar MoreControls Button ToolTip + private string quickAccessToolBarMoreControlsButtonTooltip; + + /// + /// Quick Access ToolBar MoreControls Button ToolTip + /// + public string QuickAccessToolBarMoreControlsButtonTooltip + { + get { return this.quickAccessToolBarMoreControlsButtonTooltip; } + set + { + if (this.quickAccessToolBarMoreControlsButtonTooltip != value) + { + this.quickAccessToolBarMoreControlsButtonTooltip = value; + this.RaisePropertyChanged("QuickAccessToolBarMoreControlsButtonTooltip"); + } + } + } + + #endregion + + #region Quick Access ToolBar Menu Header + + // Quick Access ToolBar Menu Header + private string quickAccessToolBarMenuHeader; + + /// + /// Quick Access ToolBar Menu Header + /// + public string QuickAccessToolBarMenuHeader + { + get { return this.quickAccessToolBarMenuHeader; } + set + { + if (this.quickAccessToolBarMenuHeader != value) + { + this.quickAccessToolBarMenuHeader = value; + this.RaisePropertyChanged("QuickAccessToolBarMenuHeader"); + } + } + } + + #endregion + + #region Quick Access ToolBar Context Menu Show Below + + // Quick Access ToolBar Minimize Quick Access Toolbar + private string quickAccessToolBarMenuShowBelow; + + /// + /// Quick Access ToolBar Minimize Quick Access Toolbar + /// + public string QuickAccessToolBarMenuShowBelow + { + get { return this.quickAccessToolBarMenuShowBelow; } + set + { + if (this.quickAccessToolBarMenuShowBelow != value) + { + this.quickAccessToolBarMenuShowBelow = value; + this.RaisePropertyChanged("QuickAccessToolBarMenuShowBelow"); + } + } + } + + #endregion + + #region Quick Access ToolBar Menu Show Above + + // Quick Access ToolBar Menu Minimize Quick Access Toolbar + private string quickAccessToolBarMenuShowAbove; + + /// + /// Quick Access ToolBar Menu Minimize Quick Access Toolbar + /// + public string QuickAccessToolBarMenuShowAbove + { + get { return this.quickAccessToolBarMenuShowAbove; } + set + { + if (this.quickAccessToolBarMenuShowAbove != value) + { + this.quickAccessToolBarMenuShowAbove = value; + this.RaisePropertyChanged("QuickAccessToolBarMenuShowAbove"); + } + } + } + + #endregion + + #region Quick Access ToolBar Menu Add Item + + // Quick Access ToolBar Menu Add Item + private string ribbonContextMenuAddItem; + + /// + /// Quick Access ToolBar Menu Add Item + /// + public string RibbonContextMenuAddItem + { + get { return this.ribbonContextMenuAddItem; } + set + { + if (this.ribbonContextMenuAddItem != value) + { + this.ribbonContextMenuAddItem = value; + this.RaisePropertyChanged("RibbonContextMenuAddItem"); + } + } + } + + #endregion + + #region Quick Access ToolBar Menu Add Group + + // Quick Access ToolBar Menu Add Group + private string ribbonContextMenuAddGroup; + + /// + /// Quick Access ToolBar Menu Add Group + /// + public string RibbonContextMenuAddGroup + { + get { return this.ribbonContextMenuAddGroup; } + set + { + if (this.ribbonContextMenuAddGroup != value) + { + this.ribbonContextMenuAddGroup = value; + this.RaisePropertyChanged("RibbonContextMenuAddGroup"); + } + } + } + + #endregion + + #region Quick Access ToolBar Menu Add Gallery + + // Quick Access ToolBar Menu Add Gallery + private string ribbonContextMenuAddGallery; + + /// + /// Quick Access ToolBar Menu Add Gallery + /// + public string RibbonContextMenuAddGallery + { + get { return this.ribbonContextMenuAddGallery; } + set + { + if (this.ribbonContextMenuAddGallery != value) + { + this.ribbonContextMenuAddGallery = value; + this.RaisePropertyChanged("RibbonContextMenuAddGallery"); + } + } + } + + #endregion + + #region Quick Access ToolBar Menu Add Menu + + // Quick Access ToolBar Menu Add Menu + private string ribbonContextMenuAddMenu; + + /// + /// Quick Access ToolBar Menu Add Menu + /// + public string RibbonContextMenuAddMenu + { + get { return this.ribbonContextMenuAddMenu; } + set + { + if (this.ribbonContextMenuAddMenu != value) + { + this.ribbonContextMenuAddMenu = value; + this.RaisePropertyChanged("RibbonContextMenuAddMenu"); + } + } + } + + #endregion + + #region Quick Access ToolBar Menu Remove Item + + // Quick Access ToolBar Menu Remove Item + private string ribbonContextMenuRemoveItem; + + /// + /// Quick Access ToolBar Menu Remove Item + /// + public string RibbonContextMenuRemoveItem + { + get { return this.ribbonContextMenuRemoveItem; } + set + { + if (this.ribbonContextMenuRemoveItem != value) + { + this.ribbonContextMenuRemoveItem = value; + this.RaisePropertyChanged("RibbonContextMenuRemoveItem"); + } + } + } + + #endregion + + #region Ribbon Context Menu Customize Quick Access Toolbar + + // Ribbon Context Menu Customize Quick Access Toolbar + private string ribbonContextMenuCustomizeQuickAccessToolbar; + + /// + /// Ribbon Context Menu Customize Quick Access Toolbar + /// + public string RibbonContextMenuCustomizeQuickAccessToolBar + { + get { return this.ribbonContextMenuCustomizeQuickAccessToolbar; } + set + { + if (this.ribbonContextMenuCustomizeQuickAccessToolbar != value) + { + this.ribbonContextMenuCustomizeQuickAccessToolbar = value; + this.RaisePropertyChanged("RibbonContextMenuCustomizeQuickAccessToolBar"); + } + } + } + + #endregion + + #region Ribbon Context Menu Customize Ribbon + + // Ribbon Context Menu Customize Quick Access Toolbar + private string ribbonContextMenuCustomizeRibbon; + + /// + /// Ribbon Context Menu Customize Quick Access Toolbar + /// + public string RibbonContextMenuCustomizeRibbon + { + get { return this.ribbonContextMenuCustomizeRibbon; } + set + { + if (this.ribbonContextMenuCustomizeRibbon != value) + { + this.ribbonContextMenuCustomizeRibbon = value; + this.RaisePropertyChanged("RibbonContextMenuCustomizeRibbon"); + } + } + } + + #endregion + + #region Ribbon Context Menu Minimize Ribbon + + // Ribbon Context Menu Minimize Quick Access Toolbar + private string ribbonContextMenuMinimizeRibbon; + + /// + /// Ribbon Context Menu Minimize Quick Access Toolbar + /// + public string RibbonContextMenuMinimizeRibbon + { + get { return this.ribbonContextMenuMinimizeRibbon; } + set + { + if (this.ribbonContextMenuMinimizeRibbon != value) + { + this.ribbonContextMenuMinimizeRibbon = value; + this.RaisePropertyChanged("RibbonContextMenuMinimizeRibbon"); + } + } + } + + #endregion + + #region Ribbon Context Menu Show Below + + // Ribbon Context Menu Minimize Quick Access Toolbar + private string ribbonContextMenuShowBelow; + + /// + /// Ribbon Context Menu Minimize Quick Access Toolbar + /// + public string RibbonContextMenuShowBelow + { + get { return this.ribbonContextMenuShowBelow; } + set + { + if (this.ribbonContextMenuShowBelow != value) + { + this.ribbonContextMenuShowBelow = value; + this.RaisePropertyChanged("RibbonContextMenuShowBelow"); + } + } + } + + #endregion + + #region Ribbon Context Menu Show Above + + // Ribbon Context Menu Minimize Quick Access Toolbar + private string ribbonContextMenuShowAbove; + + /// + /// Ribbon Context Menu Minimize Quick Access Toolbar + /// + public string RibbonContextMenuShowAbove + { + get { return this.ribbonContextMenuShowAbove; } + set + { + if (this.ribbonContextMenuShowAbove != value) + { + this.ribbonContextMenuShowAbove = value; + this.RaisePropertyChanged("RibbonContextMenuShowAbove"); + } + } + } + + #endregion + + #region ScreenTipDisableReasonHeader + + // ScreenTip's Disable Reason Header + private string screenTipDisableReasonHeader; + + /// + /// Gets or sets ScreenTip's disable reason header + /// + public string ScreenTipDisableReasonHeader + { + get { return this.screenTipDisableReasonHeader; } + set + { + if (this.screenTipDisableReasonHeader != value) + { + this.screenTipDisableReasonHeader = value; + this.RaisePropertyChanged("ScreenTipDisableReasonHeader"); + } + } + } + + #endregion + + #region ScreenTipF1Label + + // ScreenTip's Disable Reason Header + private string screenTipF1LabelHeader; + + /// + /// Gets or sets ScreenTip's disable reason header + /// + public string ScreenTipF1LabelHeader + { + get { return this.screenTipF1LabelHeader; } + set + { + if (this.screenTipF1LabelHeader != value) + { + this.screenTipF1LabelHeader = value; + this.RaisePropertyChanged("ScreenTipF1LabelHeader"); + } + } + } + + #endregion + + #region Customize Status Bar + + // Text of backstage button + private string customizeStatusBar; + + /// + /// Gets or sets customize Status Bar + /// + public string CustomizeStatusBar + { + get { return this.customizeStatusBar; } + set + { + if (this.customizeStatusBar != value) + { + this.customizeStatusBar = value; + this.RaisePropertyChanged("CustomizeStatusBar"); + } + } + } + + #endregion + + #region Initialization + + /// + /// Default constructor + /// + public RibbonLocalization() + { + // Fallback values + this.LoadEnglish(); + + this.Culture = CultureInfo.CurrentUICulture; + } + + #endregion + + #region Methods + + // Coerce all localized values + private void LoadCulture(CultureInfo culture) + { + var language = culture.TwoLetterISOLanguageName; + + switch (language) + { + case "en": + this.LoadEnglish(); + break; + + case "ru": + this.LoadRussian(); + break; + + case "uk": + this.LoadUkrainian(); + break; + + case "fa": + this.LoadPersian(); + break; + + case "de": + this.LoadGerman(); + break; + + case "hu": + this.LoadHungarian(); + break; + + case "cs": + this.LoadCzech(); + break; + + case "fr": + this.LoadFrench(); + break; + + case "pl": + this.LoadPolish(); + break; + + case "ja": + this.LoadJapanese(); + break; + + case "nl": + this.LoadDutch(); + break; + case "pt": + { + if (culture.Name == "pt-BR") + { + this.LoadPortugueseBrazilian(); + } + else + { + this.LoadPortuguese(); + } + break; + } + + case "es": + this.LoadSpanish(); + break; + + //edit by gamegear.tw + //note: add TradChinese + case "zh": + if(culture.Name=="zh-tw") + { + this.LoadTradChinese(); + }else + { + this.LoadChinese(); + } + + break; + + case "sv": + this.LoadSwedish(); + break; + + case "sk": + this.LoadSlovak(); + break; + + case "ro": + this.LoadRomanian(); + break; + + case "it": + this.LoadItalian(); + break; + + case "ar": + this.LoadArabic(); + break; + + case "da": + this.LoadDanish(); + break; + + case "az": + this.LoadAzerbaijani(); + break; + + case "fi": + this.LoadFinnish(); + break; + + case "nb": + case "nn": + case "no": + this.LoadNorwegian(); + break; + + case "tr": + this.LoadTurkish(); + break; + + case "he": + this.LoadHebrew(); + break; + + case "ge": + this.LoadGreek(); + break; + + case "ko": + this.LoadKorean(); + break; + + case "lt": + this.LoadLithuanian(); + break; + + case "vi": + this.LoadVietnamese(); + break; + + case "si": + this.LoadSinhala(); + break; + + case "sl": + this.LoadSlovenian(); + break; + + case "ca": + this.LoadCatalan(); + break; + + case "et": + this.LoadEstonian(); + break; + } + + // Coerce all values + + this.RaisePropertyChanged("BackstageButtonText"); + this.RaisePropertyChanged("BackstageButtonKeyTip"); + + this.RaisePropertyChanged("MinimizeButtonScreenTipTitle"); + this.RaisePropertyChanged("MinimizeButtonScreenTipText"); + this.RaisePropertyChanged("ExpandButtonScreenTipTitle"); + this.RaisePropertyChanged("ExpandButtonScreenTipText"); + this.RaisePropertyChanged("QuickAccessToolBarDropDownButtonTooltip"); + this.RaisePropertyChanged("QuickAccessToolBarMoreControlsButtonTooltip"); + this.RaisePropertyChanged("QuickAccessToolBarMenuHeader"); + this.RaisePropertyChanged("QuickAccessToolBarMenuShowAbove"); + this.RaisePropertyChanged("QuickAccessToolBarMenuShowBelow"); + + this.RaisePropertyChanged("RibbonContextMenuAddItem"); + this.RaisePropertyChanged("RibbonContextMenuAddGroup"); + this.RaisePropertyChanged("RibbonContextMenuAddGallery"); + this.RaisePropertyChanged("RibbonContextMenuAddMenu"); + this.RaisePropertyChanged("RibbonContextMenuRemoveItem"); + this.RaisePropertyChanged("RibbonContextMenuCustomizeRibbon"); + this.RaisePropertyChanged("RibbonContextMenuCustomizeQuickAccessToolBar"); + this.RaisePropertyChanged("RibbonContextMenuShowAbove"); + this.RaisePropertyChanged("RibbonContextMenuShowBelow"); + this.RaisePropertyChanged("RibbonContextMenuMinimizeRibbon"); + + this.RaisePropertyChanged("ScreenTipDisableReasonHeader"); + this.RaisePropertyChanged("ScreenTipF1LabelHeader"); + this.RaisePropertyChanged("CustomizeStatusBar"); + } + + #endregion + + #region English + + private void LoadEnglish() + { + // Backstage button text & key tip + this.backstageButtonText = "File"; + this.backstageButtonKeyTip = "F"; + + // See right-top corner... (two different tooltips must be if you press it) + this.minimizeButtonScreenTipTitle = "Collapse the Ribbon (Ctrl+F1)"; + this.minimizeButtonScreenTipText = "Need a bit more space? Collapse the ribbon so only the tab names show."; + this.expandButtonScreenTipTitle = "Pin the Ribbon (Ctrl+F1)"; + this.expandButtonScreenTipText = "Like seeing the ribbon? Keep it open while you work."; + + // QAT tooltips and menu items + this.quickAccessToolBarDropDownButtonTooltip = "Customize Quick Access Toolbar"; + this.quickAccessToolBarMoreControlsButtonTooltip = "More controls"; // When two arrows appear ">>" + this.quickAccessToolBarMenuHeader = "Customize Quick Access Toolbar"; + this.quickAccessToolBarMenuShowAbove = "Show Above the Ribbon"; + this.quickAccessToolBarMenuShowBelow = "Show Below the Ribbon"; + + // Click on Ribbon to show context menu + this.ribbonContextMenuAddItem = "Add to Quick Access Toolbar"; // Button + this.ribbonContextMenuAddGroup = "Add Group to Quick Access Toolbar"; // For ex., by collapsed group + this.ribbonContextMenuAddGallery = "Add Gallery to Quick Access Toolbar"; // For ex., by opened font context menu + this.ribbonContextMenuAddMenu = "Add Menu to Quick Access Toolbar"; // By dashed splitter in context menu + this.ribbonContextMenuRemoveItem = "Remove from Quick Access Toolbar"; // By item in QAT + this.ribbonContextMenuCustomizeQuickAccessToolbar = "Customize Quick Access Toolbar..."; + this.ribbonContextMenuShowBelow = "Show Quick Access Toolbar Below the Ribbon"; + this.ribbonContextMenuShowAbove = "Show Quick Access Toolbar Above the Ribbon"; + this.ribbonContextMenuCustomizeRibbon = "Customize the Ribbon..."; + this.ribbonContextMenuMinimizeRibbon = "Collapse the Ribbon"; + + // To see it in Word: open *.doc (not *.docx) and see Insert->Screenshot + // (This prop was introduced after v1.3) + //Screentips + this.screenTipDisableReasonHeader = "This command is currently disabled."; + this.screenTipF1LabelHeader = "Press F1 for help"; + + // Right-click on status bar to see it. NEW! from v2.0 + this.customizeStatusBar = "Customize Status Bar"; + } + + #endregion + + #region Russian + + private void LoadRussian() + { + this.backstageButtonText = "Файл"; + this.backstageButtonKeyTip = "Ф"; + + this.minimizeButtonScreenTipTitle = "Свернуть ленту (Ctrl + F1)"; + this.minimizeButtonScreenTipText = "Отображение или скрытие ленты\n\nКогда лента скрыта, отображаются только имена вкладок."; + this.expandButtonScreenTipTitle = "Развернуть ленту (Ctrl + F1)"; + this.expandButtonScreenTipText = "Отображение или скрытие ленты\n\nКогда лента скрыта, отображаются только имена вкладок."; + + this.quickAccessToolBarDropDownButtonTooltip = "Настройка панели быстрого доступа"; + this.quickAccessToolBarMoreControlsButtonTooltip = "Другие элементы"; + this.quickAccessToolBarMenuHeader = "Настройка панели быстрого доступа"; + this.quickAccessToolBarMenuShowAbove = "Разместить над лентой"; + this.quickAccessToolBarMenuShowBelow = "Разместить под лентой"; + + this.ribbonContextMenuAddItem = "Добавить на панель быстрого доступа"; + this.ribbonContextMenuAddGroup = "Добавить группу на панель быстрого доступа"; + this.ribbonContextMenuAddGallery = "Добавить коллекцию на панель быстрого доступа"; + this.ribbonContextMenuAddMenu = "Добавить меню на панель быстрого доступа"; + this.ribbonContextMenuRemoveItem = "Удалить с панели быстрого доступа"; + this.ribbonContextMenuCustomizeQuickAccessToolbar = "Настройка панели быстрого доступа..."; + this.ribbonContextMenuShowBelow = "Разместить панель быстрого доступа под лентой"; + this.ribbonContextMenuShowAbove = "Разместить панель быстрого доступа над лентой"; + this.ribbonContextMenuCustomizeRibbon = "Настройка ленты..."; + this.ribbonContextMenuMinimizeRibbon = "Свернуть ленту"; + + // To see it in Word: open *.doc (not *.docx) and see Insert->Screenshot + this.screenTipDisableReasonHeader = "В настоящее время эта команда отключена."; + + this.customizeStatusBar = "Настройка строки состояния"; + } + + #endregion + + #region Ukrainian + + private void LoadUkrainian() + { + // Backstage button text & key tip + this.backstageButtonText = "Файл"; + this.backstageButtonKeyTip = "Ф"; + // See right-top corner... (two different tooltips must be if you press it) + this.minimizeButtonScreenTipTitle = "Сховати Стрічку (Ctrl + F1)"; + this.minimizeButtonScreenTipText = "Показати або сховати Стрічку\n\nКоли стрічка схована, видно тільки назви вкладок"; + this.expandButtonScreenTipTitle = "Показати Стрічку (Ctrl + F1)"; + this.expandButtonScreenTipText = "Показати або сховати Стрічку\n\nКоли стрічка схована, видно тільки назви вкладок"; + // QAT tooltips and menu items + this.quickAccessToolBarDropDownButtonTooltip = "Налаштувати Панель Інструментів Швидкого Доступу"; + this.quickAccessToolBarMoreControlsButtonTooltip = "Більше елементів"; // When two arrows appear ">>" + this.quickAccessToolBarMenuHeader = "Налаштувати Панель Інструментів Швидкого Доступу"; + this.quickAccessToolBarMenuShowAbove = "Показати Поверх Стрічки"; + this.quickAccessToolBarMenuShowBelow = "Показати Знизу Стрічки"; + // Click on Ribbon to show context menu + this.ribbonContextMenuAddItem = "Додати до Панелі Інструментів Швидкого Доступу"; // Button + this.ribbonContextMenuAddGroup = "Додати Групу до Панелі Інструментів Швидкого Доступу"; // For ex., by collapsed group + this.ribbonContextMenuAddGallery = "Додати Галерею до Панелі Інструментів Швидкого Доступу"; // For ex., by opened font context menu + this.ribbonContextMenuAddMenu = "Додати Меню до Панелі Інструментів Швидкого Доступу"; // By dashed splitter in context menu + this.ribbonContextMenuRemoveItem = "Видалити з Панелі Інструментів Швидкого Доступу"; // By item in QAT + this.ribbonContextMenuCustomizeQuickAccessToolbar = "Налаштувати Панель Інструментів Швидкого Доступу..."; + this.ribbonContextMenuShowBelow = "Показати Панель Інструментів Швидкого Доступу Знизу Стрічки"; + this.ribbonContextMenuShowAbove = "Показати Панель Інструментів Швидкого Доступу Поверх Стрічки"; + this.ribbonContextMenuCustomizeRibbon = "Налаштувати Стрічку..."; + this.ribbonContextMenuMinimizeRibbon = "Зменшити Стрічку"; + + // To see it in Word: open *.doc (not *.docx) and see Insert->Screenshot + // (This prop was introduced after v1.3) + this.screenTipDisableReasonHeader = "Ця команда на даний момент недоступна."; + } + + #endregion + + #region Persian + + private void LoadPersian() + { + // Backstage button text & key tip + this.backstageButtonText = "فایل"; + this.backstageButtonKeyTip = "ف"; + + // See right-top corner... (two different tooltips must be if you press it) + this.minimizeButtonScreenTipTitle = "کوچک کردن نوار (Ctrl + F1)"; + this.minimizeButtonScreenTipText = "نمایش یا مخفی کردن نوار\n\nهنگامی که نوار مخفی است، تنها\nنام زبانه ها نمایش داده می شود."; + this.expandButtonScreenTipTitle = "بزرگ کردن نوار (Ctrl + F1)"; + this.expandButtonScreenTipText = "نمایش یا مخفی کردن نوار\n\nهنگامی که نوار مخفی است، تنها\nنام زبانه ها نمایش داده می شود."; + + // QAT tooltips and menu items + this.quickAccessToolBarDropDownButtonTooltip = "دلخواه سازی میله ابزار دسترسی سریع"; + this.quickAccessToolBarMoreControlsButtonTooltip = "ابزارهای دیگر"; // When two arrows appear ">>" + this.quickAccessToolBarMenuHeader = "دلخواه سازی میله ابزار دسترسی سریع"; + this.quickAccessToolBarMenuShowAbove = "نمایش در بالای نوار"; + this.quickAccessToolBarMenuShowBelow = "نمایش در پایین نوار"; + // Click on Ribbon to show context menu + this.ribbonContextMenuAddItem = "اضافه کردن به میله ابزار دسترسی سریع"; // Button + this.ribbonContextMenuAddGroup = "اضافه کردن گروه به میله ابزار دسترسی سریع"; // For ex., by collapsed group + this.ribbonContextMenuAddGallery = "اضافه کردن گالری به میله ابزار دسترسی سریع"; // For ex., by opened font context menu + this.ribbonContextMenuAddMenu = "اضاقه کردن منو به میله ابزار دسترسی سریع"; // By dashed splitter in context menu + this.ribbonContextMenuRemoveItem = "حذف از میله ابزار دسترسی سریع"; // By item in QAT + this.ribbonContextMenuCustomizeQuickAccessToolbar = "دلخواه سازی میله ابزار دسترسی سریع..."; + this.ribbonContextMenuShowBelow = "نمایش میله ابزار دسترسی سریع در پایین نوار"; + this.ribbonContextMenuShowAbove = "نمایش میله ابزار دسترسی سریع در بالای نوار"; + this.ribbonContextMenuCustomizeRibbon = "دلخواه سازی نوار..."; + this.ribbonContextMenuMinimizeRibbon = "کوچک کردن نوار"; + } + + #endregion + + #region German + + private void LoadGerman() + { + // Backstage button text & key tip + this.backstageButtonText = "Datei"; + this.backstageButtonKeyTip = "D"; + + // See right-top corner... (two different tooltips must be if you press it) + this.minimizeButtonScreenTipTitle = "Menüband minimieren (Strg+F1)"; + this.minimizeButtonScreenTipText = "Sie benötigen etwas mehr Platz? Reduzieren Sie das Menüband, sodass nur die Registerkartennamen angezeigt werden."; + this.expandButtonScreenTipTitle = "Menüband erweitern (Strg+F1)"; + this.expandButtonScreenTipText = "Ist es Ihnen lieber, wenn Sie das Menüband sehen? Lassen Sie es während der Arbeit geöffnet."; + + // QAT tooltips and menu items + this.quickAccessToolBarDropDownButtonTooltip = "Symbolleiste für den Schnellzugriff anpassen"; + this.quickAccessToolBarMoreControlsButtonTooltip = "Weitere Befehle…"; // When two arrows appear ">>" + this.quickAccessToolBarMenuHeader = "Symbolleiste für den Schnellzugriff anpassen"; + this.quickAccessToolBarMenuShowAbove = "Über dem Menüband anzeigen"; + this.quickAccessToolBarMenuShowBelow = "Unter dem Menüband anzeigen"; + + // Click on Ribbon to show context menu + this.ribbonContextMenuAddItem = "Zur Symbolleiste für den Schnellzugriff hinzufügen"; // Button + this.ribbonContextMenuAddGroup = "Gruppe zur Symbolleiste für den Schnellzugriff hinzufügen"; // For ex., by collapsed group + this.ribbonContextMenuAddGallery = "Katalog zur Symbolleiste für den Schnellzugriff hinzufügen"; // For ex., by opened font context menu + this.ribbonContextMenuAddMenu = "Zur Symbolleiste für den Schnellzugriff hinzufügen"; // By dashed splitter in context menu + + this.ribbonContextMenuRemoveItem = "Aus Symbolleiste für den Schnellzugriff entfernen"; // By item in QAT + this.ribbonContextMenuCustomizeQuickAccessToolbar = "Symbolleiste für den Schnellzugriff anpassen..."; + this.ribbonContextMenuShowBelow = "Symbolleiste für den Schnellzugriff unter dem Menüband anzeigen"; + this.ribbonContextMenuShowAbove = "Symbolleiste für den Schnellzugriff über dem Menüband anzeigen"; + this.ribbonContextMenuCustomizeRibbon = "Menüband anpassen..."; + this.ribbonContextMenuMinimizeRibbon = "Menüband minimieren"; + + //Screentips + this.screenTipDisableReasonHeader = "Diese Funktion ist momentan deaktiviert."; + this.screenTipF1LabelHeader = "Drücken Sie F1 für die Hilfe"; + + // Right-click on status bar to see it. NEW! from v2.0 + this.customizeStatusBar = "Statusleiste anpassen"; + } + + #endregion + + #region Hungarian + + private void LoadHungarian() + { + // Backstage button text & key tip + this.backstageButtonText = "Fájl"; + this.backstageButtonKeyTip = "F"; + + // See right-top corner... (two different tooltips must be if you press it) + this.minimizeButtonScreenTipTitle = "A menüszalag összecsukása (Ctrl + F1)"; + this.minimizeButtonScreenTipText = "Csak a lapnevek megjelenítése a menüszalagon"; + this.expandButtonScreenTipTitle = "Menüszalag kibontása (Ctrl + F1)"; + this.expandButtonScreenTipText = "A menüszalag megjelenítése úgy, hogy egy parancsra kattintás után is látható maradjon"; + + // QAT tooltips and menu items + this.quickAccessToolBarDropDownButtonTooltip = "Gyorselérési eszköztár testreszabása"; + this.quickAccessToolBarMoreControlsButtonTooltip = "További vezérlők"; // When two arrows appear ">>" + this.quickAccessToolBarMenuHeader = "Gyorselérési eszköztár testreszabása"; + this.quickAccessToolBarMenuShowAbove = "Megjelenítés a menüszalag alatt"; + this.quickAccessToolBarMenuShowBelow = "Megjelenítés a menüszalag felett"; + + // Click on Ribbon to show context menu + this.ribbonContextMenuAddItem = "Felvétel a gyorselérési eszköztárra"; // Button + this.ribbonContextMenuAddGroup = "Felvétel a gyorselérési eszköztárra"; // For ex., by collapsed group + this.ribbonContextMenuAddGallery = "Gyűjtemény felvétele a gyorselérési eszköztárra"; // For ex., by opened font context menu + this.ribbonContextMenuAddMenu = "Felvétel a gyorselérési eszköztárra"; // By dashed splitter in context menu + this.ribbonContextMenuRemoveItem = "Eltávolítás a gyorselérési eszköztárról"; // By item in QAT + this.ribbonContextMenuCustomizeQuickAccessToolbar = "Gyorselérési eszköztár testreszabása..."; + this.ribbonContextMenuShowBelow = "A gyorselérési eszköztár megjelenítése a menüszalag alatt"; + this.ribbonContextMenuShowAbove = "A gyorselérési eszköztár megjelenítése a menüszalag felett"; + this.ribbonContextMenuCustomizeRibbon = "Menüszalag testreszabása..."; + this.ribbonContextMenuMinimizeRibbon = " A menüszalag összecsukása"; + + // To see it in Word: open *.doc (not *.docx) and see Insert->Screenshot + // (This prop was introduced after v1.3) + this.screenTipDisableReasonHeader = "Ez a parancs jelenleg nem használható."; + } + + #endregion + + #region Czech + + private void LoadCzech() + { + // Backstage button text & key tip + this.backstageButtonText = "Soubor"; + this.backstageButtonKeyTip = "S"; + + // See right-top corner... (two different tooltips must be if you press it) + this.minimizeButtonScreenTipTitle = "Skrýt pás karet (Ctrl + F1)"; + this.minimizeButtonScreenTipText = "Zobrazit nebo skrýt pás karet\n\nJe-li pás karet skrytý, jsou zobrazeny pouze názvy karet"; + this.expandButtonScreenTipTitle = "Zobrazit pás karet (Ctrl + F1)"; + this.expandButtonScreenTipText = "Zobrazit nebo skrýt pás karet\n\nJe-li pás karet skrytý, jsou zobrazeny pouze názvy karet"; + + // QAT tooltips and menu items + this.quickAccessToolBarDropDownButtonTooltip = "Přizpůsobit panel nástrojů Rychlý přístup"; + this.quickAccessToolBarMoreControlsButtonTooltip = "Další příkazy"; // When two arrows appear ">>" + this.quickAccessToolBarMenuHeader = "Přizpůsobit panel nástrojů Rychlý přístup"; + this.quickAccessToolBarMenuShowAbove = "Zobrazit nad pásem karet"; + this.quickAccessToolBarMenuShowBelow = "Zobrazit pod pásem karet"; + + // Click on Ribbon to show context menu + this.ribbonContextMenuAddItem = "Přidat na panel nástrojů Rychlý přístup"; // Button + this.ribbonContextMenuAddGroup = "Přidat na panel nástrojů Rychlý přístup"; // For ex., by collapsed group + this.ribbonContextMenuAddGallery = "Přidat galerii na panel nástrojů Rychlý přístup"; // For ex., by opened font context menu + this.ribbonContextMenuAddMenu = "Přidat na panel nástrojů Rychlý přístup"; // By dashed splitter in context menu + this.ribbonContextMenuRemoveItem = "Odebrat z panelu nástrojů Rychlý přístup"; // By item in QAT + this.ribbonContextMenuCustomizeQuickAccessToolbar = "Přizpůsobit panel nástrojů Rychlý přístup..."; + this.ribbonContextMenuShowBelow = "Zobrazit panel nástrojů Rychlý přístup pod pásem karet"; + this.ribbonContextMenuShowAbove = "Zobrazit panel nástrojů Rychlý přístup nad pásem karet"; + this.ribbonContextMenuCustomizeRibbon = "Přizpůsobit pás karet..."; + this.ribbonContextMenuMinimizeRibbon = "Skrýt pás karet"; + + //Screentips + this.screenTipDisableReasonHeader = "Tento příkaz je aktuálně zakázán."; + this.screenTipF1LabelHeader = "Stiskni F1 pro nápovědu"; + + // Right-click on status bar to see it. NEW! from v2.0 + this.customizeStatusBar = "Přizpůsobit Status Bar"; + } + + #endregion + + #region French + + private void LoadFrench() + { + // Backstage button text & key tip + this.backstageButtonText = "Fichier"; + this.backstageButtonKeyTip = "F"; + + // See right-top corner... (two different tooltips must be if you press it) + this.minimizeButtonScreenTipTitle = "Minimiser le Ruban (Ctrl + F1)"; + this.minimizeButtonScreenTipText = "Afficher ou masquer le Ruban \n\nQuand le Ruban est masqué, seul les noms sont affichés"; + this.expandButtonScreenTipTitle = "Agrandir le Ruban (Ctrl + F1)"; + this.expandButtonScreenTipText = "Afficher ou masquer le Ruban \n\nQuand le Ruban est masqué, seul les noms sont affichés"; + + // QAT tooltips and menu items + this.quickAccessToolBarDropDownButtonTooltip = "Personnaliser la barre d'outils Accès Rapide"; + this.quickAccessToolBarMoreControlsButtonTooltip = "Plus de contrôles"; // When two arrows appear ">>" + this.quickAccessToolBarMenuHeader = "Personnaliser la barre d'outil Accès Rapide"; + this.quickAccessToolBarMenuShowAbove = "Afficher au dessus du Ruban"; + this.quickAccessToolBarMenuShowBelow = "Afficher en dessous du Ruban"; + + // Click on Ribbon to show context menu + this.ribbonContextMenuAddItem = "Ajouter un élément à la barre d'outils Accès Rapide"; // Button + this.ribbonContextMenuAddGroup = "Ajouter un groupe à la barre d'outils Accès Rapide"; // For ex., by collapsed group + this.ribbonContextMenuAddGallery = "Ajouter une galerie à la barre d'outils Accès Rapide"; // For ex., by opened font context menu + this.ribbonContextMenuAddMenu = "Ajouter un menu à la barre d'outils Accès Rapide"; // By dashed splitter in context menu + this.ribbonContextMenuRemoveItem = "Supprimer de la barre d'outils Accès Rapide"; // By item in QAT + this.ribbonContextMenuCustomizeQuickAccessToolbar = "Personnaliser la barre d'outils Accès Rapide..."; + this.ribbonContextMenuShowBelow = "Afficher la barre d'outils Accès Rapide en dessous du Ruban"; + this.ribbonContextMenuShowAbove = "Afficher la barre d'outils Accès Rapide au dessus du Ruban"; + this.ribbonContextMenuCustomizeRibbon = "Personnaliser le Ruban..."; + this.ribbonContextMenuMinimizeRibbon = "Minimiser le Ruban"; + this.customizeStatusBar = "Personnaliser la barre de statut"; + + // To see it in Word: open *.doc (not *.docx) and see Insert->Screenshot + this.screenTipDisableReasonHeader = "Cette commande est actuellement désactivée."; + } + + #endregion + + #region Polish + + private void LoadPolish() + { + // Backstage button text & key tip + this.backstageButtonText = "Plik"; + this.backstageButtonKeyTip = "P"; + + // See right-top corner... (two different tooltips must be if you press it) + this.minimizeButtonScreenTipTitle = "Minimalizuj Wstążkę (Ctrl + F1)"; + this.minimizeButtonScreenTipText = "Pokazuje lub ukrywa Wstążkę\n\nGdy Wstążka jest ukryta, tylko nazwy zakładek są widoczne"; + this.expandButtonScreenTipTitle = "Rozwiń Wstążkę (Ctrl + F1)"; + this.expandButtonScreenTipText = "Pokazuje lub ukrywa Wstążkę\n\nGdy Wstążka jest ukryta, tylko nazwy zakładek są widoczne"; + + // QAT tooltips and menu items + this.quickAccessToolBarDropDownButtonTooltip = "Dostosuj pasek narzędzi Szybki dostęp"; + this.quickAccessToolBarMoreControlsButtonTooltip = "Więcej poleceń..."; // When two arrows appear ">>" + this.quickAccessToolBarMenuHeader = "Dostosuj pasek narzędzi Szybki dostęp"; + this.quickAccessToolBarMenuShowAbove = "Pokaż powyżej Wstążki"; + this.quickAccessToolBarMenuShowBelow = "Pokaż poniżej Wstążki"; + + // Click on Ribbon to show context menu + this.ribbonContextMenuAddItem = "Dodaj do paska narzędzi Szybki dostęp"; // Button + this.ribbonContextMenuAddGroup = "Dodaj Grupę do paska narzędzi Szybki dostęp"; // For ex., by collapsed group + this.ribbonContextMenuAddGallery = "Dodaj Galerię do paska narzędzi Szybki dostęp"; // For ex., by opened font context menu + this.ribbonContextMenuAddMenu = "Dodaj do paska narzędzi Szybki dostęp"; // By dashed splitter in context menu + this.ribbonContextMenuRemoveItem = "Usuń z paska narzędzi Szybki dostęp"; // By item in QAT + this.ribbonContextMenuCustomizeQuickAccessToolbar = "Dostosuj pasek narzędzi Szybki dostęp..."; + this.ribbonContextMenuShowBelow = "Pokaż pasek Szybki dostęp poniżej Wstążki"; + this.ribbonContextMenuShowAbove = "Pokaż pasek Szybki dostęp powyżej Wstążki"; + this.ribbonContextMenuCustomizeRibbon = "Dostosuj Wstążkę..."; + this.ribbonContextMenuMinimizeRibbon = "Minimalizuj Wstążkę"; + } + + #endregion + + #region Japanese + + private void LoadJapanese() + { + // Backstage button text & key tip + this.backstageButtonText = "ファイル"; + this.backstageButtonKeyTip = "F"; + + // See right-top corner... (two different tooltips must be if you press it) + this.minimizeButtonScreenTipTitle = "リボンの最小化 (Ctrl + F1)"; + this.minimizeButtonScreenTipText = "リボンの表示/非表示を切り替えます。\n\nリボンを非表示にすると、タブ名のみが表示されます。"; + this.expandButtonScreenTipTitle = "リボンの展開 (Ctrl + F1)"; + this.expandButtonScreenTipText = "リボンの表示/非表示を切り替えます。\n\nリボンを非表示にすると、タブ名のみが表示されます。"; + + // QAT tooltips and menu items + this.quickAccessToolBarDropDownButtonTooltip = "クイック アクセス ツール バーのユーザー設定"; + this.quickAccessToolBarMoreControlsButtonTooltip = "その他のボタン"; // When two arrows appear ">>" + this.quickAccessToolBarMenuHeader = "クイック アクセス ツール バーのユーザー設定"; + this.quickAccessToolBarMenuShowAbove = "リボンの上に表示"; + this.quickAccessToolBarMenuShowBelow = "リボンの下に表示"; + + // Click on Ribbon to show context menu + this.ribbonContextMenuAddItem = "クイック アクセス ツール バーに追加"; // Button + this.ribbonContextMenuAddGroup = "グループをクイック アクセス ツール バーに追加"; // For ex., by collapsed group + this.ribbonContextMenuAddGallery = "ギャラリーをクイック アクセス ツール バーに追加"; // For ex., by opened font context menu + this.ribbonContextMenuAddMenu = "メニューをクイック アクセス ツール バーに追加"; // By dashed splitter in context menu + this.ribbonContextMenuRemoveItem = "クイック アクセス ツール バーから削除"; // By item in QAT + this.ribbonContextMenuCustomizeQuickAccessToolbar = "クイック アクセス ツール バーのユーザー設定..."; + this.ribbonContextMenuShowBelow = "クイック アクセス ツール バーをリボンの下に表示"; + this.ribbonContextMenuShowAbove = "クイック アクセス ツール バーをリボンの上に表示"; + this.ribbonContextMenuCustomizeRibbon = "リボンのユーザー設定..."; + this.ribbonContextMenuMinimizeRibbon = "リボンの最小化"; + this.customizeStatusBar = "ステータス バーのユーザー設定"; + + this.screenTipDisableReasonHeader = "このコマンドは現在無効になっています"; + } + + #endregion + + #region Dutch + + private void LoadDutch() + { + // Backstage button text & key tip + this.backstageButtonText = "Bestand"; + this.backstageButtonKeyTip = "B"; + // See right-top corner... (two different tooltips must be if you press it) + this.minimizeButtonScreenTipTitle = "Het lint minimaliseren (Ctrl + F1)"; + this.minimizeButtonScreenTipText = "Verberg of laat het lint zien\n\nWanneer het lint verborgen is, zijn alleen de tabulatie namen zichtbaar"; + this.expandButtonScreenTipTitle = "Het lint Maximaliseren (Ctrl + F1)"; + this.expandButtonScreenTipText = "Verberg of laat het lint zien\n\nWanneer het lint verborgen is, zijn alleen de tabulatie namen zichtbaar"; + + // QAT tooltips and menu items + this.quickAccessToolBarDropDownButtonTooltip = "Werkbalk snelle toegang aanpassen"; + this.quickAccessToolBarMoreControlsButtonTooltip = "meer opdrachten"; // When two arrows appear ">>" + this.quickAccessToolBarMenuHeader = " Werkbalk snelle toegang aanpassen "; + this.quickAccessToolBarMenuShowAbove = "Boven het lint weergeven"; + this.quickAccessToolBarMenuShowBelow = "beneden het lint weergeven"; + // Click on Ribbon to show context menu + this.ribbonContextMenuAddItem = "Menu toevoegen aan werkbalk snelle toegang"; // Button + this.ribbonContextMenuAddGroup = "Groep toevoegen aan werkbalk snelle toegang"; // For ex., by collapsed group + this.ribbonContextMenuAddGallery = "Galerij toevoegen aan werkbalk snelle toegang"; // For ex., by opened font context menu + this.ribbonContextMenuAddMenu = " Menu toevoegen aan werkbalk snelle toegang "; // By dashed splitter in context menu + this.ribbonContextMenuRemoveItem = " Verwijder uit werkbalk snelle toegang "; // By item in QAT + this.ribbonContextMenuCustomizeQuickAccessToolbar = "Customize Quick Access Toolbar..."; + this.ribbonContextMenuShowBelow = " Werkbalk snelle toegang onder het lint weergeven"; + this.ribbonContextMenuShowAbove = " Werkbalk snelle toegang boven het lint weergeven "; + this.ribbonContextMenuCustomizeRibbon = "Lint aanpassen..."; + this.ribbonContextMenuMinimizeRibbon = " Het lint minimaliseren"; + } + + #endregion + + #region Brazilian + + private void LoadPortugueseBrazilian() + { + // Backstage button text & key tip + this.backstageButtonText = "Arquivo"; + this.backstageButtonKeyTip = "A"; + + // See right-top corner... (two different tooltips must be if you press it) + this.minimizeButtonScreenTipTitle = "Minimizar o Ribbon (Ctrl + F1)"; + this.minimizeButtonScreenTipText = "Mostrar ou esconder o Ribbon\n\nQuando o Ribbon estiver escondido, somente o nome das abas serão mostrados"; + this.expandButtonScreenTipTitle = "Expandir o Ribbon (Ctrl + F1)"; + this.expandButtonScreenTipText = "Mostrar ou esconder o Ribbon\n\nQuando o Ribbon estiver escondido, somente o nome das abas serão mostrados"; + + // QAT tooltips and menu items + this.quickAccessToolBarDropDownButtonTooltip = "Customizar Barra de acesso rápido"; + this.quickAccessToolBarMoreControlsButtonTooltip = "Mais controles"; // When two arrows appear ">>" + this.quickAccessToolBarMenuHeader = " Customizar Barra de acesso rápido"; + this.quickAccessToolBarMenuShowAbove = "Mostrar acima do Ribbon"; + this.quickAccessToolBarMenuShowBelow = "Mostrar abaixo do Ribbon"; + + // Click on Ribbon to show context menu + this.ribbonContextMenuAddItem = "Adicionar para Barra de acesso rápido"; // Button + this.ribbonContextMenuAddGroup = " Adicionar o grupo para Barra de acesso rápido"; // For ex., by collapsed group + this.ribbonContextMenuAddGallery = "Adicionar a galeria para Barra de acesso rápido"; + // For ex., by opened font context menu + this.ribbonContextMenuAddMenu = " Adicionar o menu para Barra de acesso rápido"; + // By dashed splitter in context menu + this.ribbonContextMenuRemoveItem = "Remover da Barra de acesso rápido"; // By item in QAT + this.ribbonContextMenuCustomizeQuickAccessToolbar = "Customizar Barra de acesso rápido..."; + this.ribbonContextMenuShowBelow = "Mostrar Barra de acesso rápido abaixo do Ribbon"; + this.ribbonContextMenuShowAbove = "Mostrar Barra de acesso rápido acima do Ribbon"; + this.ribbonContextMenuCustomizeRibbon = "Customizar o Ribbon..."; + this.ribbonContextMenuMinimizeRibbon = "Minimizar o Ribbon"; + + // To see it in Word: open *.doc (not *.docx) and see Insert->Screenshot + // (This prop was introduced after v1.3) + this.screenTipDisableReasonHeader = "Este comando está desativado."; + } + + #endregion + + #region Spanish + + private void LoadSpanish() + { + // Backstage button text & key tip + this.backstageButtonText = "Archivo"; + this.backstageButtonKeyTip = "A"; + + // See right-top corner... (two different tooltips must be if you press it) + // TRANSLATOR'S NOTE: This block is not shown at Windows 7's Apps (WordPad or Paint) + this.minimizeButtonScreenTipTitle = "Minimizar la cinta (Ctrl + F1)"; + this.minimizeButtonScreenTipText = "Muestra u oculta la cinta\n\nCuando la cinta está oculta, sólo se muestran los nombres de las pestañas"; + this.expandButtonScreenTipTitle = "Expandir la cinta (Ctrl + F1)"; + this.expandButtonScreenTipText = "Muestra u oculta la cinta\n\nCuando la cinta está oculta, sólo se muestran los nombres de las pestañas"; + + // QAT tooltips and menu items + this.quickAccessToolBarDropDownButtonTooltip = "Personalizar barra de herramientas de acceso rápido"; + this.quickAccessToolBarMoreControlsButtonTooltip = "Más controles"; // When two arrows appear ">>" + this.quickAccessToolBarMenuHeader = "Personalizar barra de herramientas de acceso rápido"; + this.quickAccessToolBarMenuShowAbove = "Mostrar sobre la cinta"; + this.quickAccessToolBarMenuShowBelow = "Mostrar bajo la cinta"; + + // Click on Ribbon to show context menu + this.ribbonContextMenuAddItem = "Agregar a la barra de herramientas de acceso rápido"; // Button + this.ribbonContextMenuAddGroup = "Agregar grupo a la barra de herramientas de acceso rápido"; // For ex., by collapsed group + this.ribbonContextMenuAddGallery = "Agregar galería a la barra de herramientas de acceso rápido"; // For ex., by opened font context menu + this.ribbonContextMenuAddMenu = "Agregar menú a la barra de herramientas de acceso rápido"; // By dashed splitter in context menu + this.ribbonContextMenuRemoveItem = "Quitar de la barra de herramientas de acceso rápido"; // By item in QAT + this.ribbonContextMenuCustomizeQuickAccessToolbar = "Personalizar la barra de herramientas de acceso rápido..."; + this.ribbonContextMenuShowBelow = "Mostrar barra de herramientas de acceso rápido bajo la cinta"; + this.ribbonContextMenuShowAbove = "Mostrar barra de herramientas de acceso rápido sobre la cinta"; + this.ribbonContextMenuCustomizeRibbon = "Personalizar la cinta..."; + this.ribbonContextMenuMinimizeRibbon = "Minimizar la cinta"; + + //Screentips + this.screenTipDisableReasonHeader = "Este comando está desactivado actualmente"; + this.screenTipF1LabelHeader = "Pulse F1 para obtener más ayuda"; + + // Right-click on status bar to see it. NEW! from v2.0 + this.customizeStatusBar = "Personalizar barra de estado"; + } + + #endregion + + #region Chinese + + private void LoadChinese() + { + // Backstage button text & key tip + this.backstageButtonText = "文件"; + this.backstageButtonKeyTip = "F"; + + // See right-top corner... (two different tooltips must be if you press it) + this.minimizeButtonScreenTipTitle = "功能区最小化 (Ctrl + F1)"; + this.minimizeButtonScreenTipText = "仅显示功能区上的选项卡名称。单击选项卡可显示命令。"; + this.expandButtonScreenTipTitle = "展开功能区 (Ctrl + F1)"; + this.expandButtonScreenTipText = "始终显示功能区选项卡和命令。"; + + // QAT tooltips and menu items + this.quickAccessToolBarDropDownButtonTooltip = "自定义快速访问具栏"; + this.quickAccessToolBarMoreControlsButtonTooltip = "其他命令"; // When two arrows appear ">>" + this.quickAccessToolBarMenuHeader = "自定义快速访问工具栏"; + this.quickAccessToolBarMenuShowAbove = "在功能区上方显示"; + this.quickAccessToolBarMenuShowBelow = "在功能区下方显示"; + + // Click on Ribbon to show context menu + this.ribbonContextMenuAddItem = "添加到快速访问工具栏"; // Button + this.ribbonContextMenuAddGroup = "在快速访问工具栏中添加组"; // For ex., by collapsed group + this.ribbonContextMenuAddGallery = "在快速访问工具栏中添加样式"; // For ex., by opened font context menu + this.ribbonContextMenuAddMenu = "在快速访问工具栏中添加菜单"; // By dashed splitter in context menu + this.ribbonContextMenuRemoveItem = "在快速访问工具栏中移除"; // By item in QAT + this.ribbonContextMenuCustomizeQuickAccessToolbar = "自定义快速访问工具栏..."; + this.ribbonContextMenuShowBelow = "在功能区下方显示快速访问工具栏"; + this.ribbonContextMenuShowAbove = "在功能区上方显示快速访问工具栏"; + this.ribbonContextMenuCustomizeRibbon = "自定义功能区..."; + this.ribbonContextMenuMinimizeRibbon = "功能区最小化"; + + // To see it in Word: open *.doc (not *.docx) and see Insert->Screenshot + // (This prop was introduced after v1.3) + this.screenTipDisableReasonHeader = "此命令当前已被禁用。"; + + // Right-click on status bar to see it. NEW! from v2.0 + this.customizeStatusBar = "自定义状态栏"; + } + + #endregion + + #region Swedish + + private void LoadSwedish() + { + // Backstage button text & key tip + this.backstageButtonText = "Arkiv"; + this.backstageButtonKeyTip = "A"; + + // See right-top corner... (two different tooltips must be if you press it) + this.minimizeButtonScreenTipTitle = "Minimera menyfliksområdet (Ctrl + F1)"; + this.minimizeButtonScreenTipText = "Visa eller göm menyfliksområdet \n\nNär menyfliksområdet är dolt, visas endast flikarna"; + this.expandButtonScreenTipTitle = "Expandera menyfliksområdet (Ctrl + F1)"; + this.expandButtonScreenTipText = "Visa eller göm menyfliksområdet \n\nNär menyfliksområdet är dolt, visas endast flikarna"; + + // QAT tooltips and menu items + this.quickAccessToolBarDropDownButtonTooltip = "Anpassa verktygsfältet Snabbåtkomst "; + this.quickAccessToolBarMoreControlsButtonTooltip = "Fler kommandon"; // When two arrows appear ">>" + this.quickAccessToolBarMenuHeader = " Anpassa verktygsfältet Snabbåtkomst"; + this.quickAccessToolBarMenuShowAbove = "Visa ovanför menyfliksområdet"; + this.quickAccessToolBarMenuShowBelow = "Visa under menyfliksområdet"; + + // Click on Ribbon to show context menu + this.ribbonContextMenuAddItem = "Lägg till i verktygsfältet Snabbåtkomst"; // Button + this.ribbonContextMenuAddGroup = "Lägg till i verktygsfältet Snabbåtkomst"; // For ex., by collapsed group + this.ribbonContextMenuAddGallery = "Lägg till galleriet i verktygsfältet Snabbåtkomst"; // For ex., by opened font context menu + this.ribbonContextMenuAddMenu = " Lägg till menyn i verktygsfältet Snabbåtkomst "; // By dashed splitter in context menu + this.ribbonContextMenuRemoveItem = "Ta bort från verktygsfältet Snabbåtkomst"; // By item in QAT + this.ribbonContextMenuCustomizeQuickAccessToolbar = "Anpassa verktygsfältet Snabbåtkomst..."; + this.ribbonContextMenuShowBelow = " Visa verktygsfältet Snabbåtkomst under menyfliksområdet"; + this.ribbonContextMenuShowAbove = " Visa verktygsfältet Snabbåtkomst ovanför menyfliksområdet "; + this.ribbonContextMenuCustomizeRibbon = "Anpassa menyfliksområdet..."; + this.ribbonContextMenuMinimizeRibbon = "Minimera menyfliksområdet"; + } + + #endregion + + #region Slovak + + private void LoadSlovak() + { + // Backstage button text & key tip + this.backstageButtonText = "Súbor"; + this.backstageButtonKeyTip = "S"; + + // See right-top corner... (two different tooltips must be if you press it) + this.minimizeButtonScreenTipTitle = "Skryť pás s nástrojmi (Ctrl + F1)"; + this.minimizeButtonScreenTipText = "Zobraziť alebo skryť pás s nástrojmi\n\nKeď je pás s nástrojmi skrytý, sú zobrazené iba názvy kariet"; + this.expandButtonScreenTipTitle = "Zobraziť pás s nástrojmi (Ctrl + F1)"; + this.expandButtonScreenTipText = " Zobraziť alebo skryť pás s nástrojmi\n\nKeď je pás s nástrojmi skrytý, sú zobrazené iba názvy kariet "; + + // QAT tooltips and menu items + this.quickAccessToolBarDropDownButtonTooltip = "Prispôsobenie panela s nástrojmi Rýchly prístup"; + this.quickAccessToolBarMoreControlsButtonTooltip = "Ďalšie príkazy"; // When two arrows appear ">>" + this.quickAccessToolBarMenuHeader = "Prispôsobenie panela s nástrojmi Rýchly prístup"; + this.quickAccessToolBarMenuShowAbove = " Zobraziť nad pásom s nástrojmi "; + this.quickAccessToolBarMenuShowBelow = "Zobraziť pod pásom s nástrojmi"; + + // Click on Ribbon to show context menu + this.ribbonContextMenuAddItem = "Pridať na panel s nástrojmi Rýchly prístup"; // Button + this.ribbonContextMenuAddGroup = " Pridať na panel s nástrojmi Rýchly prístup "; // For ex., by collapsed group + this.ribbonContextMenuAddGallery = " Pridať galériu do panela s nástrojmi Rýchly prístup "; // For ex., by opened font context menu + this.ribbonContextMenuAddMenu = "Pridať na panel s nástrojmi Rýchly prístup"; // By dashed splitter in context menu + this.ribbonContextMenuRemoveItem = "Odstrániť z panela s nástrojmi Rýchly prístup "; // By item in QAT + this.ribbonContextMenuCustomizeQuickAccessToolbar = " Prispôsobenie panela s nástrojmi Rýchly prístup..."; + this.ribbonContextMenuShowBelow = "Panel s nástrojmi Rýchly prístup zobraziť pod panelom s nástrojmi"; + this.ribbonContextMenuShowAbove = "Panel s nástrojmi Rýchly prístup zobraziť nad panelom s nástrojmi "; + this.ribbonContextMenuCustomizeRibbon = "Prispôsobenie panela s nástrojmi Rýchly prístup..."; + this.ribbonContextMenuMinimizeRibbon = "Minimalizovať pás s nástrojmi"; + } + + #endregion + + #region Romanian + + private void LoadRomanian() + { + // Backstage button text & key tip + this.backstageButtonText = "Fișier"; + this.backstageButtonKeyTip = "F"; + + // See right-top corner... (two different tooltips must be if you press it) + this.minimizeButtonScreenTipTitle = "Minimizează Ribbon-ul (Ctrl + F1)"; + this.minimizeButtonScreenTipText = "Afișează sau ascunde Ribbon-ul\nCând Ribbon-ul este ascuns, sunt afișate doar numele taburilor"; + this.expandButtonScreenTipTitle = "Expandează Ribbon-ul (Ctrl + F1)"; + this.expandButtonScreenTipText = "Afișează sau ascunde Ribbon-ul\nCând Ribbon-ul este ascuns, sunt afișate doar numele taburilor"; + + // QAT tooltips and menu items + this.quickAccessToolBarDropDownButtonTooltip = "Personalizează Bara de Acces Rapid"; + this.quickAccessToolBarMoreControlsButtonTooltip = "Mai multe controale"; // When two arrows appear ">>" + this.quickAccessToolBarMenuHeader = "Personalizează Bara de Acces Rapid"; + this.quickAccessToolBarMenuShowAbove = "Afișează peste Ribbon"; + this.quickAccessToolBarMenuShowBelow = "Afișează sub Ribbon"; + + // Click on Ribbon to show context menu + this.ribbonContextMenuAddItem = "Adaugă la Bara de Acess Rapid"; // Button + this.ribbonContextMenuAddGroup = "Adaugă Grupul la Bara de Acess Rapid"; // For ex., by collapsed group + this.ribbonContextMenuAddGallery = "Adaugă Galeria la Bara de Acess Rapid"; + // For ex., by opened font context menu + this.ribbonContextMenuAddMenu = "Adaugă Meniul la Bara de Acess Rapid"; // By dashed splitter in context menu + this.ribbonContextMenuRemoveItem = "Eimină din Bara de Acess Rapid"; // By item in QAT + this.ribbonContextMenuCustomizeQuickAccessToolbar = "Personalizează Bara de Acces Rapid..."; + this.ribbonContextMenuShowBelow = "Afișează Bara de Acces Rapid sub Ribbon"; + this.ribbonContextMenuShowAbove = "Afișează Bara de Acces Rapid peste Ribbon"; + this.ribbonContextMenuCustomizeRibbon = "Personalizează Ribbon-ul..."; + this.ribbonContextMenuMinimizeRibbon = "Minimizează Ribbon-ul..."; + + // To see it in Word: open *.doc (not *.docx) and see Insert->Screenshot + // (This prop was introduced after v1.3) + this.screenTipDisableReasonHeader = "Această comandă nu este disponibilă momentan."; + } + + #endregion + + #region Italian + + private void LoadItalian() + { + // Backstage button text & key tip + this.backstageButtonText = "File"; + this.backstageButtonKeyTip = "F"; + + // See right-top corner... (two different tooltips must be if you press it) + this.minimizeButtonScreenTipTitle = "Riduci a icona barra multifunzione (Ctrl + F1)"; + this.minimizeButtonScreenTipText = "Consente di visualizzare solo i nomi delle schede nella barra multifunzione."; + this.expandButtonScreenTipTitle = "Espandi la barra multifunzione (Ctrl + F1)"; + this.expandButtonScreenTipText = "Visualizza la barra multifunzione in modo che rimanga sempre espansa, anche se l’utente ha fatto click su un comando."; + + // QAT tooltips and menu items + this.quickAccessToolBarDropDownButtonTooltip = "Personalizza barra di accesso rapido"; + this.quickAccessToolBarMoreControlsButtonTooltip = "Altri comandi…"; // When two arrows appear ">>" + this.quickAccessToolBarMenuHeader = "Personalizza barra di accesso rapido"; + this.quickAccessToolBarMenuShowAbove = "Mostra sopra la barra multifunzione"; + this.quickAccessToolBarMenuShowBelow = "Mostra sotto la barra multifunzione"; + + // Click on Ribbon to show context menu + this.ribbonContextMenuAddItem = "Aggiungi alla barra di accesso rapido"; // Button + this.ribbonContextMenuAddGroup = "Aggiungi gruppo alla barra di accesso rapido"; // For ex., by collapsed group + this.ribbonContextMenuAddGallery = "Aggiungi raccolta alla barra di accesso rapido"; // For ex., by opened font context menu + this.ribbonContextMenuAddMenu = "Aggiungi menu alla barra di accesso rapido"; // By dashed splitter in context menu + this.ribbonContextMenuRemoveItem = "Rimuovi dalla barra di accesso rapido"; // By item in QAT + this.ribbonContextMenuCustomizeQuickAccessToolbar = "Personalizza barra di accesso rapido..."; + this.ribbonContextMenuShowBelow = "Mostra la barra di accesso rapido sotto la barra multifunzione"; + this.ribbonContextMenuShowAbove = "Mostra la barra di accesso rapido sopra la barra multifunzione"; + this.ribbonContextMenuCustomizeRibbon = "Personalizza barra multifunzione..."; + this.ribbonContextMenuMinimizeRibbon = "Riduci a icona barra multifunzione"; + + // To see it in Word: open *.doc (not *.docx) and see Insert->Screenshot + // (This prop was introduced after v1.3) + this.screenTipDisableReasonHeader = "Questo commando è disattivato."; + } + + #endregion + + #region Arabic + + private void LoadArabic() + { + // Backstage button text & key tip + this.backstageButtonText = "ملف "; + this.backstageButtonKeyTip = "م "; + + // See right-top corner... (two different tooltips must be if you press it) + this.minimizeButtonScreenTipTitle = "(Ctrl + F1)تصغير الشريط "; + this.minimizeButtonScreenTipText = "إظهار أسماء علامات التبويب فقط على الشريط."; + this.expandButtonScreenTipTitle = "(Ctrl + F1)توسيع الشريط "; + this.expandButtonScreenTipText = "إظهار الشريط بحيث يكون موسعاً دائماً حتى بعد النقر فوق أمر."; + + // QAT tooltips and menu items + this.quickAccessToolBarDropDownButtonTooltip = "تخصيص شريط أدوات الوصول السريع"; + this.quickAccessToolBarMoreControlsButtonTooltip = "أوامر إضافية"; // When two arrows appear ">>" + this.quickAccessToolBarMenuHeader = "تخصيص شريط أدوات الوصول السريع"; + this.quickAccessToolBarMenuShowAbove = "إظهار أعلى الشريط"; + this.quickAccessToolBarMenuShowBelow = "إظهار أسفل الشريط"; + + // Click on Ribbon to show context menu + this.ribbonContextMenuAddItem = "إضافة إلى شريط أدوات الوصول السريع"; // Button + this.ribbonContextMenuAddGroup = "إضافة إلى شريط أدوات الوصول السريع"; // For ex., by collapsed group + this.ribbonContextMenuAddGallery = "إضافة إلى شريط أدوات الوصول السريع"; // For ex., by opened font context menu + this.ribbonContextMenuAddMenu = "إضافة إلى شريط أدوات الوصول السريع"; // By dashed splitter in context menu + this.ribbonContextMenuRemoveItem = "إزالة إلى شريط أدوات الوصول السريع"; // By item in QAT + this.ribbonContextMenuCustomizeQuickAccessToolbar = "تخصيص شريط أدوات الوصول السريع..."; + this.ribbonContextMenuShowBelow = "إظهار شريط أدوات الوصول السريع أسفل الشريط"; + this.ribbonContextMenuShowAbove = "إظهار شريط أدوات الوصول السريع أعلى الشريط"; + this.ribbonContextMenuCustomizeRibbon = "تخصيص الشريط..."; + this.ribbonContextMenuMinimizeRibbon = "تصغير الشريط"; + + // To see it in Word: open *.doc (not *.docx) and see Insert->Screenshot + // (This prop was introduced after v1.3) + this.screenTipDisableReasonHeader = "تم حالياً تعطيل هذا الأمر."; + } + + #endregion + + #region Danish + + private void LoadDanish() + { + // Backstage button text & key + this.backstageButtonText = "Filer"; + this.backstageButtonKeyTip = "F"; + + // See right-top corner... (two different tooltips must be if you press it) + this.minimizeButtonScreenTipTitle = "Minimer båndet (Ctrl + F1)"; + this.minimizeButtonScreenTipText = "Vis kun fanenavnene på båndet."; + this.expandButtonScreenTipTitle = "Udvid båndet (Ctrl + F1)"; + this.expandButtonScreenTipText = "Vis båndet, så det altid er udvidet, selv når du klikker på en kommando."; + + // QAT tooltips and menu items + this.quickAccessToolBarDropDownButtonTooltip = "Tilpas værktøjslinjen Hurtig adgang"; + this.quickAccessToolBarMoreControlsButtonTooltip = "Flere kontrolelementer"; // When two arrows appear ">>" + this.quickAccessToolBarMenuHeader = " Tilpas værktøjslinjen Hurtig adgang"; + this.quickAccessToolBarMenuShowAbove = "Vis ovenover båndet"; + this.quickAccessToolBarMenuShowBelow = "Vis under båndet"; + + // Click on Ribbon to show context menu + this.ribbonContextMenuAddItem = "Føj til værktøjslinjen Hurtig adgang"; // Button + this.ribbonContextMenuAddGroup = "Føj til værktøjslinjen Hurtig adgang"; // For ex., by collapsed group + this.ribbonContextMenuAddGallery = "Tilføj Galleri til værktøjslinjen Hurtig adgang"; // For ex., by opened font context menu + this.ribbonContextMenuAddMenu = "Føj til værktøjslinjen Hurtig adgang"; // By dashed splitter in context menu + this.ribbonContextMenuRemoveItem = "Fjern fra værktøjslinjen Hurtig adgang"; // By item in QAT + this.ribbonContextMenuCustomizeQuickAccessToolbar = "Tilpas værktøjslinjen Hurtig adgang..."; + this.ribbonContextMenuShowBelow = "Vis værktøjslinjen Hurtig adgang under båndet"; + this.ribbonContextMenuShowAbove = "Vis værktøjslinjen Hurtig adgang ovenover båndet"; + this.ribbonContextMenuCustomizeRibbon = "Tilpas båndet..."; + this.ribbonContextMenuMinimizeRibbon = "Minimer båndet"; + + // To see it in Word: open *.doc (not *.docx) and see Insert->Screenshot + // (This prop was introduced after v1.3) + this.screenTipDisableReasonHeader = "Denne kommando er aktuelt deaktiveret."; + } + + #endregion + + #region Portuguese + + private void LoadPortuguese() + { + // Backstage button text & key tip + this.backstageButtonText = "Ficheiro"; + this.backstageButtonKeyTip = "F"; + + // See right-top corner... (two different tooltips must be if you press it) + this.minimizeButtonScreenTipTitle = "Minimizar o Friso (Ctrl + F1)"; + this.minimizeButtonScreenTipText = "Mostrar apenas os nomes dos separadores no Frisos."; + this.expandButtonScreenTipTitle = "Expandir o Friso (Ctrl + F1)"; + this.expandButtonScreenTipText = "Mostrar o Friso de modo a aparecer sempre expandido mesmo depois de clicar num comando."; + + // QAT tooltips and menu items + this.quickAccessToolBarDropDownButtonTooltip = "Personalizar Barra de Ferramentas de Acesso Rápido"; + this.quickAccessToolBarMoreControlsButtonTooltip = "Mais Comandos..."; // When two arrows appear ">>" + this.quickAccessToolBarMenuHeader = "Personalizar Barra de Ferramentas de Acesso Rápido"; + this.quickAccessToolBarMenuShowAbove = "Mostrar Acima do Friso"; + this.quickAccessToolBarMenuShowBelow = "Mostrar Abaixo do Friso"; + + // Click on Ribbon to show context menu + this.ribbonContextMenuAddItem = "Adicionar à Barra de Ferramentas de Acesso Rápido"; + this.ribbonContextMenuAddGroup = "Adicionar Grupo à Barra de Ferramentas de Acesso Rápido"; + this.ribbonContextMenuAddGallery = "Adicionar Galeria à Barra de Ferramentas de Acesso Rápido"; + this.ribbonContextMenuAddMenu = "Adicionar Menu à Barra de Ferramentas de Acesso Rápido"; + this.ribbonContextMenuRemoveItem = "Remover da Barra de Ferramentas de Acesso Rápido"; + this.ribbonContextMenuCustomizeQuickAccessToolbar = "Personalizar Barra de Ferramentas de Acesso Rápido..."; + this.ribbonContextMenuShowBelow = "Mostrar Barra de Ferramentas de Acesso Rápido Abaixo do Friso"; + this.ribbonContextMenuShowAbove = "Mostrar Barra de Ferramentas de Acesso Rápido Acima do Friso"; + this.ribbonContextMenuCustomizeRibbon = "Personalizar o Friso..."; + this.ribbonContextMenuMinimizeRibbon = "Minimizar o Friso"; + + // To see it in Word: open *.doc (not *.docx) and see Insert->Screenshot + // (This prop was introduced after v1.3) + this.screenTipDisableReasonHeader = "Este comando está desactivado actualmente."; + } + + #endregion + + #region Azerbaijani + + private void LoadAzerbaijani() + { + // Backstage button text & key tip + this.backstageButtonText = "Fayl"; + this.backstageButtonKeyTip = "F"; + + // See right-top corner... (two different tooltips must be if you press it) + this.minimizeButtonScreenTipTitle = "Menyu lentini kiçilt (Ctrl + F1)"; + this.minimizeButtonScreenTipText = "Menyu lentini göstər və ya gizlət\n\nMenyu lentini kiçiləndə, yalnız tabların adları göstərilir"; + this.expandButtonScreenTipTitle = "Menyu lentini böyüt(Ctrl + F1)"; + this.expandButtonScreenTipText = " Menyu lentini göstər və ya gizlət\n\nMenyu lentini gizldəndə, yalnız, tabların adları göstərilir"; + + // QAT tooltips and menu items + this.quickAccessToolBarDropDownButtonTooltip = "Sürətli Keçidin Alətlərini fərdiləşdir"; + this.quickAccessToolBarMoreControlsButtonTooltip = "Digər nəzarət vasitələri"; // When two arrows appear ">>" + this.quickAccessToolBarMenuHeader = " Sürətli Keçidin Alətlərini fərdiləşdir "; + this.quickAccessToolBarMenuShowAbove = "Menyu lentinin üstündə göstər"; + this.quickAccessToolBarMenuShowBelow = " Menyu lentinin altında göstər "; + + // Click on Ribbon to show context menu + this.ribbonContextMenuAddItem = "Sürətli Keçidin Alətlərinə əlavə et"; // Button + this.ribbonContextMenuAddGroup = " Sürətli Keçidin Alətlərinə Qrup əlavə et "; // For ex., by collapsed group + this.ribbonContextMenuAddGallery = " Sürətli Keçidin Alətlərinə Qalereya əlavə et"; // For ex., by opened font context menu + this.ribbonContextMenuAddMenu = " Sürətli Keçidin Alətlərinə Menyu əlavə et"; // By dashed splitter in context menu + this.ribbonContextMenuRemoveItem = " Sürətli Keçidin Alətlərindən sil"; // By item in QAT + this.ribbonContextMenuCustomizeQuickAccessToolbar = " Sürətli Keçidin Alətlərini fərdiləşdir..."; + this.ribbonContextMenuShowBelow = " Sürətli Keçidin Alətlərini Menyu lentinin altında göstər "; + this.ribbonContextMenuShowAbove = " Sürətli Keçidin Alətlərini Menyu lentinin üstündə göstər "; + this.ribbonContextMenuCustomizeRibbon = "Menyu lentini fərdiləşdir..."; + this.ribbonContextMenuMinimizeRibbon = " Menyu lentini kiçilt"; + } + + #endregion + + #region Finnish + + private void LoadFinnish() + { + this.backstageButtonText = "Tiedosto"; + this.backstageButtonKeyTip = "T"; + this.minimizeButtonScreenTipTitle = "Pienennä valintanauha (Ctrl + F1)"; + this.minimizeButtonScreenTipText = "Näytä valintanauhassa vain välilehtien nimet"; + this.expandButtonScreenTipTitle = "Laajenna valintanauha (Ctrl + F1)"; + this.expandButtonScreenTipText = "Näytä valintanauha aina laajennettuna silloinkin, kun valitset komennon"; + this.quickAccessToolBarDropDownButtonTooltip = "Mukauta pikatyökaluriviä"; + this.quickAccessToolBarMoreControlsButtonTooltip = "Lisää valintoja"; + this.quickAccessToolBarMenuHeader = "Mukauta pikatyökaluriviä"; + this.quickAccessToolBarMenuShowAbove = "Näytä valintanauhan yläpuolella"; + this.quickAccessToolBarMenuShowBelow = "Näytä valintanauhan alapuolella"; + this.ribbonContextMenuAddItem = "Lisää pikatyökaluriville"; + this.ribbonContextMenuAddGroup = "Lisää ryhmä pikatyökaluriviin"; + this.ribbonContextMenuAddGallery = "Lisää valikoima pikatyökaluriviin"; + this.ribbonContextMenuAddMenu = "Lisää valikko pikatyökaluriviin"; + this.ribbonContextMenuRemoveItem = "Poista pikatyökaluriviltä"; + this.ribbonContextMenuCustomizeQuickAccessToolbar = "Mukauta pikatyökaluriviä..."; + this.ribbonContextMenuShowBelow = "Näytä pikatyökalurivi valintanauhan alapuolella"; + this.ribbonContextMenuShowAbove = "Näytä pikatyökalurivi valintanauhan yläpuolella"; + this.ribbonContextMenuCustomizeRibbon = "Mukauta valintanauhaa..."; + this.ribbonContextMenuMinimizeRibbon = "Pienennä valintanauha"; + this.screenTipDisableReasonHeader = "Tämä komento on tällä hetkellä poissa käytöstä"; + } + + #endregion + + #region Norwegian + + private void LoadNorwegian() + { + // Backstage button text & key tip + this.backstageButtonText = "Fil"; + this.backstageButtonKeyTip = "F"; + + // See right-top corner... (two different tooltips must be if you press it) + this.minimizeButtonScreenTipTitle = "Minimer båndet (Ctrl + F1)"; + this.minimizeButtonScreenTipText = "Viser bare kategorinavnene på båndet"; + this.expandButtonScreenTipTitle = "Utvider båndet (Ctrl + F1)"; + this.expandButtonScreenTipText = "Vis båndet slik at det alltid er utvidet selv etter at du har valgt en kommando"; + + // QAT tooltips and menu items + this.quickAccessToolBarDropDownButtonTooltip = "Tilpass verktøylinje for hurtigtilgang"; + this.quickAccessToolBarMoreControlsButtonTooltip = "Flere kontroller"; // When two arrows appear ">>" + this.quickAccessToolBarMenuHeader = "Tilpass verktøylinje for hurtigtilgang"; + this.quickAccessToolBarMenuShowAbove = "Vis over båndet"; + this.quickAccessToolBarMenuShowBelow = "Vis under båndet"; + + // Click on Ribbon to show context menu + this.ribbonContextMenuAddItem = "Legg til på verktøylinje for hurtigtilgang"; // Button + this.ribbonContextMenuAddGroup = "Legg til gruppe på verktøylinje for hurtigtilgang"; // For ex., by collapsed group + this.ribbonContextMenuAddGallery = "Legg til galleri på verktøylinje for hurtigtilgang"; // For ex., by opened font context menu + this.ribbonContextMenuAddMenu = "Legg til meny på verktøylinje for hurtigtilgang"; // By dashed splitter in context menu + this.ribbonContextMenuRemoveItem = "Fjern verktøylinjen for hurtigtilgang"; // By item in QAT + this.ribbonContextMenuCustomizeQuickAccessToolbar = "Tilpass verktøylinje for hurtigtilgang..."; + this.ribbonContextMenuShowBelow = "Vis verktøylinjen for hurtigtilgang under båndet"; + this.ribbonContextMenuShowAbove = "Vis verktøylinjen for hurtigtilgang over båndet"; + this.ribbonContextMenuCustomizeRibbon = "Tilpass båndet..."; + this.ribbonContextMenuMinimizeRibbon = "Minimer båndet"; + + // To see it in Word: open *.doc (not *.docx) and see Insert->Screenshot + // (This prop was introduced after v1.3) + this.screenTipDisableReasonHeader = "Denne kommandoen er for øyeblikket deaktivert."; + } + + #endregion + + #region Turkish + + private void LoadTurkish() + { + // Backstage button text & key tip + this.backstageButtonText = "Dosya"; + this.backstageButtonKeyTip = "D"; + + // See right-top corner... (two different tooltips must be if you press it) + this.minimizeButtonScreenTipTitle = "Şeridi Daralt (Ctrl+F1)"; + this.minimizeButtonScreenTipText = "Daha fazla alana mı\nihtiyacınız var? Şeridi daraltın, yalnızca sekme isimleri görünsün."; + this.expandButtonScreenTipTitle = "Şeridi Sabitle (Ctrl+F1)"; + this.expandButtonScreenTipText = "Şeridi görmek mi istiyorsunuz? Çalışırken açık tutun."; + + // QAT tooltips and menu items + this.quickAccessToolBarDropDownButtonTooltip = "Hızlı Erişim Araç Çubuğu'nu Özelleştir"; + this.quickAccessToolBarMoreControlsButtonTooltip = "Diğer denetimler"; // When two arrows appear ">>" + this.quickAccessToolBarMenuHeader = "Hızlı Erişim Araç Çubuğu'nu Özelleştir"; + this.quickAccessToolBarMenuShowAbove = "Şeridin Üstünde Göster"; + this.quickAccessToolBarMenuShowBelow = "Şeridin Altında Göster"; + + // Click on Ribbon to show context menu + this.ribbonContextMenuAddItem = "Hızlı Erişim Araç Çubuğu'na Ekle"; // Button + this.ribbonContextMenuAddGroup = "Grubu Hızlı Erişim Araç Çubuğu'na Ekle"; // For ex., by collapsed group + this.ribbonContextMenuAddGallery = "Galeriyi Hızlı Erişim Araç Çubuğu'na Ekle"; // For ex., by opened font context menu + this.ribbonContextMenuAddMenu = "Menüyü Hızlı Erişim Araç Çubuğu'na Ekle"; // By dashed splitter in context menu + this.ribbonContextMenuRemoveItem = "Hızlı Erişim Araç Çubuğu'ndan Kaldır"; // By item in QAT + this.ribbonContextMenuCustomizeQuickAccessToolbar = "Hızlı Erişim Araç Çubuğu'nu Özelleştir"; + this.ribbonContextMenuShowBelow = "Hızlı Erişim Araç Çubuğu'nu Şeridin Altında Göster"; + this.ribbonContextMenuShowAbove = "Hızlı Erişim Araç Çubuğu'nu Şeridin Üstünde Göster"; + this.ribbonContextMenuCustomizeRibbon = "Şeridi Özelleştir..."; + this.ribbonContextMenuMinimizeRibbon = "Şeridi Daralt"; + + // To see it in Word: open *.doc (not *.docx) and see Insert->Screenshot + // (This prop was introduced after v1.3) + this.screenTipDisableReasonHeader = "Bu komut şu anda devre dışı"; + this.screenTipF1LabelHeader = "Yardım için F1'e basın."; + + // Right-click on status bar to see it. NEW! from v2.0 + this.customizeStatusBar = "Durum Çubuğunu Özelleştir"; + } + + #endregion + + #region Hebrew + + private void LoadHebrew() + { + // Backstage button text & key tip + this.backstageButtonText = "קובץ"; + this.backstageButtonKeyTip = "ק"; + + // See right-top corner... (two different tooltips must be if you press it) + this.minimizeButtonScreenTipTitle = "מזער את רצועת הכלים (Ctrl + F1)"; + this.minimizeButtonScreenTipText = "הצג רק את שמות הכרטיסיות\nברצועת הכלים."; + this.expandButtonScreenTipTitle = "הרחב את רצועת הכלים (Ctrl + F1)"; + this.expandButtonScreenTipText = "הצג את רצועת הכלים כשהיא\nמורחבת תמיד, גם לאחר\nשתלחץ על הפקודה."; + + // QAT tooltips and menu items + this.quickAccessToolBarDropDownButtonTooltip = "התאמה אישית של סרגל הכלים לגישה מהירה"; + this.quickAccessToolBarMoreControlsButtonTooltip = "פקודות נוספות"; // When two arrows appear ">>" + this.quickAccessToolBarMenuHeader = "התאמה אישית של סרגל הכלים לגישה מהירה"; + this.quickAccessToolBarMenuShowAbove = "הצג מעל לרצועת הכלים"; + this.quickAccessToolBarMenuShowBelow = "הצג מעל לרצועת הכלים"; + + // Click on Ribbon to show context menu + this.ribbonContextMenuAddItem = "הוסף לסרגל הכלים לגישה מהירה"; // Button + this.ribbonContextMenuAddGroup = "הוסף קבוצה לסרגל הכלים לגישה מהירה"; // For ex., by collapsed group + this.ribbonContextMenuAddGallery = "הוסף גלריה לסרגל הכלים לגישה מהירה"; // For ex., by opened font context menu + this.ribbonContextMenuAddMenu = "הוסף תפריט לסרגל הכלים לגישה מהירה"; // By dashed splitter in context menu + this.ribbonContextMenuRemoveItem = "הסר מסרגל הכלים לגישה מהירה"; // By item in QAT + this.ribbonContextMenuCustomizeQuickAccessToolbar = "של סרגל הכלים רצועת הכלים..."; + this.ribbonContextMenuShowBelow = "הצג את סרגל הכלים לגישה מהירה מתחת לרצועת הכלים"; + this.ribbonContextMenuShowAbove = "הצג את סרגל הכלים לגישה מהירה מעל לרצועת הכלים"; + this.ribbonContextMenuCustomizeRibbon = "התאמה אישית של רצועת הכלים..."; + this.ribbonContextMenuMinimizeRibbon = "מזער את רצועת הכלים"; + + // To see it in Word: open *.doc (not *.docx) and see Insert->Screenshot + // (This prop was introduced after v1.3) + this.screenTipDisableReasonHeader = "פקודה זו אינה זמינה כעת."; + + // Right-click on status bar to see it. NEW! from v2.0 + this.customizeStatusBar = "התאמה אישית של שורת המצב"; + } + + #endregion + + #region Greek + + private void LoadGreek() + { + // Backstage button text & key tip + this.backstageButtonText = "Αρχείο"; + this.backstageButtonKeyTip = "Α"; + + // See right-top corner... (two different tooltips must be if you press it) + this.minimizeButtonScreenTipTitle = "Ελαχιστοποίηση της Κορδέλας (Ctrl + F1)"; + this.minimizeButtonScreenTipText = "Εμφάνιση μόνο των ονομάτων καρτελών στην Κορδέλα."; + this.expandButtonScreenTipTitle = "Ανάπτυξη της Κορδέλας (Ctrl + F1)"; + this.expandButtonScreenTipText = "Εμφάνιση της Κορδέλας προκειμένου να αναπτύσσεται πάντα, ακόμα και αφού κάνετε κλικ σε μια εντολή."; + + // QAT tooltips and menu items + this.quickAccessToolBarDropDownButtonTooltip = "Προσαρμογή γραμμής εργαλείων γρήγορης πρόσβασης"; + this.quickAccessToolBarMoreControlsButtonTooltip = "Περισσότερες εντολές"; // When two arrows appear ">>" + this.quickAccessToolBarMenuHeader = "Προσαρμογή γραμμής εργαλείων γρήγορης πρόσβασης"; + this.quickAccessToolBarMenuShowAbove = "Εμφάνιση πάνω από την Κορδέλα"; + this.quickAccessToolBarMenuShowBelow = "Εμφάνιση κάτω από την Κορδέλα"; + + // Click on Ribbon to show context menu + this.ribbonContextMenuAddItem = "Προσθήκη στη γραμμή εργαλείων γρήγορης πρόσβασης"; // Button + this.ribbonContextMenuAddGroup = "Προσθήκη ομάδας στη γραμμή εργαλείων γρήγορης πρόσβασης"; // For ex., by collapsed group + this.ribbonContextMenuAddGallery = "Προσθήκη συλλογής στη γραμμή εργαλείων γρήγορης πρόσβασης"; // For ex., by opened font context menu + this.ribbonContextMenuAddMenu = "Προσθήκη μενού στη γραμμή εργαλείων γρήγορης πρόσβασης"; // By dashed splitter in context menu + this.ribbonContextMenuRemoveItem = "Κατάργηση από τη γραμμή εργαλείων γρήγορης πρόσβασης"; // By item in QAT + this.ribbonContextMenuCustomizeQuickAccessToolbar = "Προσαρμογή γραμμής εργαλείων γρήγορης πρόσβασης..."; + this.ribbonContextMenuShowBelow = "Εμφάνιση της γραμμής εργαλείων γρήγορης πρόσβασης κάτω από την Κορδέλα"; + this.ribbonContextMenuShowAbove = "Εμφάνιση της γραμμής εργαλείων γρήγορης πρόσβασης πάνω από την Κορδέλα"; + this.ribbonContextMenuCustomizeRibbon = "Προσαρμογή της Κορδέλας..."; + this.ribbonContextMenuMinimizeRibbon = "Ελαχιστοποίηση της Κορδέλας"; + + // To see it in Word: open *.doc (not *.docx) and see Insert->Screenshot + // (This prop was introduced after v1.3) + this.screenTipDisableReasonHeader = "Αυτή η εντολή είναι απενεργοποιημένη προς το παρόν."; + + // Right-click on status bar to see it. NEW! from v2.0 + this.customizeStatusBar = "Προσαρμογή γραμμής κατάστασης"; + } + + #endregion + + #region Korean + + private void LoadKorean() + { + // Backstage button text & key tip + this.backstageButtonText = "파일"; + this.backstageButtonKeyTip = "F"; + + // See right-top corner... (two different tooltips must be if you press it) + this.minimizeButtonScreenTipTitle = "리본 메뉴를 최소화 합니다 (Ctrl + F1)"; + this.minimizeButtonScreenTipText = "리본 메뉴를 표시하거나 숨깁니다\n\n리본 메뉴가 숨김 상태일때만,\n탭이름이 보여집니다"; + this.expandButtonScreenTipTitle = "리본 메뉴를 표시합니다 (Ctrl + F1)"; + this.expandButtonScreenTipText = "리본 메뉴를 표시하거나 숨깁니다\n\n리본 메뉴가 숨김 상태일때만,\n탭이름이 보여집니다"; + + // QAT tooltips and menu items + this.quickAccessToolBarDropDownButtonTooltip = "빠른 실행 도구 모음 사용자 지정"; + this.quickAccessToolBarMoreControlsButtonTooltip = "기타 컨트롤들"; // When two arrows appear ">>" + this.quickAccessToolBarMenuHeader = "빠른 실행 도구 모음 사용자 지정"; + this.quickAccessToolBarMenuShowAbove = "리본 메뉴 위에 표시"; + this.quickAccessToolBarMenuShowBelow = "리본 메뉴 아래에 표시"; + + // Click on Ribbon to show context menu + this.ribbonContextMenuAddItem = "빠른 실행 도구 모음에 추가"; // Button + this.ribbonContextMenuAddGroup = "그룹을 빠른 실행 도구 모음에 추가"; // For ex., by collapsed group + this.ribbonContextMenuAddGallery = "갤러리를 빠른 실행 도구 모음에 추가"; // For ex., by opened font context menu + this.ribbonContextMenuAddMenu = "메뉴를 빠른 실행 도구 모음에 추가"; // By dashed splitter in context menu + this.ribbonContextMenuRemoveItem = "빠른 실행 도구 모음에서 단추 제거"; // By item in QAT + this.ribbonContextMenuCustomizeQuickAccessToolbar = "빠른 실행 도구 모음 사용자 지정..."; + this.ribbonContextMenuShowBelow = "리본 메뉴 아래에 빠른 실행 도구 모음 표시"; + this.ribbonContextMenuShowAbove = "리본 메뉴 위에 빠른 실행 도구 모음 표시"; + this.ribbonContextMenuCustomizeRibbon = "리본 메뉴 사용자 지정..."; + this.ribbonContextMenuMinimizeRibbon = "리본 메뉴 최소화"; + + // To see it in Word: open *.doc (not *.docx) and see Insert->Screenshot + // (This prop was introduced after v1.3) + this.screenTipDisableReasonHeader = "이 명령은 현재 사용할 수 없습니다."; + + // Right-click on status bar to see it. NEW! from v2.0 + this.customizeStatusBar = "상태 표시줄 사용자 지정"; + } + + #endregion + + #region Lithuanian + + private void LoadLithuanian() + { + // Backstage button text & key tip + this.backstageButtonText = "Failas"; + this.backstageButtonKeyTip = "F"; + + // See right-top corner... (two different tooltips must be if you press it) + this.minimizeButtonScreenTipTitle = "Minimizuoti juostelę (Ctrl + F1)"; + this.minimizeButtonScreenTipText = "Juostelėje rodyti tik skirtukų pavadinimus."; + this.expandButtonScreenTipTitle = "Išplėsti juostelę (Ctrl + F1)"; + this.expandButtonScreenTipText = "Rodyti juostelę taip, kad visada butų išskleista net ir spustelėjus komandą."; + + // QAT tooltips and menu items + + this.quickAccessToolBarDropDownButtonTooltip = "Tinkinti sparčiosios prieigos įrankių juostą"; + this.quickAccessToolBarMoreControlsButtonTooltip = "Daugiau valdiklių"; // When two arrows appear ">>" + this.quickAccessToolBarMenuHeader = "Tinkinti sparčiosios prieigos įrankių juostą"; + this.quickAccessToolBarMenuShowAbove = "Rodyti virš juostelės"; + this.quickAccessToolBarMenuShowBelow = "Rodyti po juostele"; + + // Click on Ribbon to show context menu + this.ribbonContextMenuAddItem = "Pridėti į sparčiosios prieigos įrankių juostą"; // Button + this.ribbonContextMenuAddGroup = "Pridėti į sparčiosios prieigos įrankių juostą"; + this.ribbonContextMenuAddGallery = "Įtraukti galeriją į sparčiosios prieigos įrankių juostą"; // For ex., by opened font context menu + this.ribbonContextMenuAddMenu = "Pridėti į sparčiosios prieigos įrankių juostą"; + this.ribbonContextMenuRemoveItem = "Šalinti iš sparčiosios prieigos įrankių juostos"; // By item in QAT + this.ribbonContextMenuCustomizeQuickAccessToolbar = "Tinkinti sparčiosios prieigos įrankių juostą..."; + this.ribbonContextMenuShowBelow = "Rodyti po juostele"; + this.ribbonContextMenuShowAbove = "Rodyti virš juostelės"; + this.ribbonContextMenuCustomizeRibbon = "Tinkinti juostelę:"; + this.ribbonContextMenuMinimizeRibbon = "Minimizuoti juostelę"; + } + + #endregion + + #region Vietnamese + + private void LoadVietnamese() + { + // Backstage button text & key tip + this.backstageButtonText = "Tệp"; + this.backstageButtonKeyTip = "F"; + + // See right-top corner... (two different tooltips must be if you press it) + this.minimizeButtonScreenTipTitle = "Thu gọn Ruy băng (Ctrl + F1)"; + this.minimizeButtonScreenTipText = "Hiện hoặc ẩn Ruy băng\n\nKhi Ruy băng ẩn, chỉ có tên thẻ được hiện"; + this.expandButtonScreenTipTitle = "Mở rộng Ruy băng (Ctrl + F1)"; + this.expandButtonScreenTipText = "Hiện hoặc ẩn Ruy băng\n\nKhi Ruy băng ẩn, chỉ có tên thẻ được hiện"; + + // QAT tooltips and menu items + this.quickAccessToolBarDropDownButtonTooltip = "Tùy chỉnh thanh công cụ Truy cập nhanh"; + this.quickAccessToolBarMoreControlsButtonTooltip = "Thêm điều khiển"; // khi có hai mũi tên ">>" + this.quickAccessToolBarMenuHeader = "Tùy chỉnh thanh công cụ Truy cập nhanh"; + this.quickAccessToolBarMenuShowAbove = "Hiện trên thanh Ruy băng"; + this.quickAccessToolBarMenuShowBelow = "Hiện dưới thanh Ruy băng"; + + // Click on Ribbon to show context menu + this.ribbonContextMenuAddItem = "Thêm vào thanh công cụ Truy cập nhanh"; // Button + this.ribbonContextMenuAddGroup = "Thêm nhóm vào thanh công cụ Truy cập nhanh"; // For ex., by collapsed group + this.ribbonContextMenuAddGallery = "Thêm bộ sưu tập vào thanh công cụ Truy cập nhanh"; // For ex., by opened font context menu + this.ribbonContextMenuAddMenu = "Thêm menu vào thanh công cụ Truy cập nhanh"; // By dashed splitter in context menu + this.ribbonContextMenuRemoveItem = "Loại"; // By item in QAT + this.ribbonContextMenuCustomizeQuickAccessToolbar = "Tùy chỉnh thanh công cụ Truy cập nhanh..."; + this.ribbonContextMenuShowBelow = "Hiện thanh công cụ truy cập nhanh dưới thanh Ruy băng"; + this.ribbonContextMenuShowAbove = "Hiện thanh công cụ truy cập nhanh trên thanh Ruy băng"; + this.ribbonContextMenuCustomizeRibbon = "Tùy biến thanh Ruy băng..."; + this.ribbonContextMenuMinimizeRibbon = "Thu gọn Ruy băng"; + + // To see it in Word: open *.doc (not *.docx) and see Insert->Screenshot + // (This prop was introduced after v1.3) + this.screenTipDisableReasonHeader = "Lệnh này hiện bị tắt."; + + // Right-click on status bar to see it. NEW! from v2.0 + this.customizeStatusBar = "Tùy biến thanh Trạng thái"; + } + + #endregion + + #region Sinhala (Sri Lanka) + + private void LoadSinhala() + { + // Backstage button text & key tip + this.backstageButtonText = "ගොනුව"; + this.backstageButtonKeyTip = "න1"; + + // See right-top corner... (two different tooltips must be if you press it) + this.minimizeButtonScreenTipTitle = "රිබනය හකුළන්න (Ctrl + F1)"; + this.minimizeButtonScreenTipText = "රිබනය මත පටිති නාම පමණක් පෙන්වන්න."; + this.expandButtonScreenTipTitle = "රිබනය විහිදන්න (Ctrl + F1)"; + this.expandButtonScreenTipText = "රිබනය පෙන්වන්න, එවිට ඔබ\n\n විධානයක් ක්ලික් කළද එය\n\n සැමවිටම විහිදී පවතී."; + + // QAT tooltips and menu items + this.quickAccessToolBarDropDownButtonTooltip = "ඉක්මන් ප්‍රෙව්ශ මෙවලම් තීරය අභිමත කරණය"; + this.quickAccessToolBarMoreControlsButtonTooltip = "තවත් විධාන"; // When two arrows appear ">>" + this.quickAccessToolBarMenuHeader = "ඉක්මන් ප්‍රෙව්ශ මෙවලම් තීරය අභිමත කරණය"; + this.quickAccessToolBarMenuShowAbove = "රිබනයට ඉහලින් පෙන්වන්න"; + this.quickAccessToolBarMenuShowBelow = "රිබනයට පහලින් පෙන්වන්න"; + + // Click on Ribbon to show context menu + this.ribbonContextMenuAddItem = "ඉක්මන් ප්‍රෙව්ශ මෙවලම් තීරයට එක් කරන්න"; // Button + this.ribbonContextMenuAddGroup = "ඉක්මන් ප්‍රෙව්ශ මෙවලම් තීරයට එක් කරන්න"; // For ex., by collapsed group + this.ribbonContextMenuAddGallery = "ඉක්මන් ප්‍රෙව්ශ මෙවලම් තීරයට ගැලරිය එක් කරන්න"; // For ex., by opened font context menu + this.ribbonContextMenuAddMenu = "ඉක්මන් ප්‍රෙව්ශ මෙවලම් තීරයට මෙනුව එක් කරන්න"; // By dashed splitter in context menu + this.ribbonContextMenuRemoveItem = "ඉක්මන් ප්‍රෙව්ශ මෙවලම් තීරයෙන් ඉවත් කරන්න"; // By item in QAT + this.ribbonContextMenuCustomizeQuickAccessToolbar = "ඉක්මන් ප්‍රෙව්ශ මෙවලම් තීරය අභිමත කරණය කරන්න..."; + this.ribbonContextMenuShowBelow = "ඉක්මන් ප්‍රෙව්ශ මෙවලම් තීරය රිබනයට පහලින් පෙන්වන්න"; + this.ribbonContextMenuShowAbove = "ඉක්මන් ප්‍රෙව්ශ මෙවලම් තීරය රිබනයට ඉහලින් පෙන්වන්න"; + this.ribbonContextMenuCustomizeRibbon = "රිබනය අභිමත කරණය කරන්න..."; + this.ribbonContextMenuMinimizeRibbon = "රිබනය හකුළන්න"; + + // To see it in Word: open *.doc (not *.docx) and see Insert->Screenshot + // (This prop was introduced after v1.3) + this.screenTipDisableReasonHeader = "මෙම විධානය දැනට භාවිතා කළ නොහැක"; + + // Right-click on status bar to see it. NEW! from v2.0 + this.customizeStatusBar = "තත්ව තීරුව අභිමත කරණය"; + } + + #endregion + + #region Slovenian + + private void LoadSlovenian() + { + // Backstage button text & key tip + this.backstageButtonText = "Datoteka"; + this.backstageButtonKeyTip = "D"; + + // See right-top corner... (two different tooltips must be if you press it) + this.minimizeButtonScreenTipTitle = "Minimiraj trak (Ctrl + F1)"; + this.minimizeButtonScreenTipText = "Pokaži ali skrij trak\n\nKo je trak skrit, so prikazani samo zavihki"; + this.expandButtonScreenTipTitle = "Razširi trak (Ctrl + F1)"; + this.expandButtonScreenTipText = "Pokaži ali skrij trak\n\nKo je trak skrit, so prikazani samo zavihki"; + + // QAT tooltips and menu items + this.quickAccessToolBarDropDownButtonTooltip = "Prilagodi orodno vrstico za hitri dostop"; + this.quickAccessToolBarMoreControlsButtonTooltip = "Več ukazov"; // When two arrows appear ">>" + this.quickAccessToolBarMenuHeader = "Prilagodi orodno vrstico za hitri dostop"; + this.quickAccessToolBarMenuShowAbove = "Pokaži nad trakom"; + this.quickAccessToolBarMenuShowBelow = "Pokaži pod trakom"; + + // Click on Ribbon to show context menu + this.ribbonContextMenuAddItem = "Dodaj v orodno vrstico za hitri dostop"; // Button + this.ribbonContextMenuAddGroup = "Dodaj skupino orodni vrstici za hitri dostop"; // For ex., by collapsed group + this.ribbonContextMenuAddGallery = "Dodaj galerijo orodni vrstici za hitri dostop"; // For ex., by opened font context menu + this.ribbonContextMenuAddMenu = "Dodaj meni orodni vrstici za hitri dostop"; // By dashed splitter in context menu + this.ribbonContextMenuRemoveItem = "Odstrani iz orodne vrstice za hitri dostop"; // By item in QAT + this.ribbonContextMenuCustomizeQuickAccessToolbar = "Prilagodi orodno vrstico za hitri dostop..."; + this.ribbonContextMenuShowBelow = "Pokaži orodno vrstico za hitri dostop pod trakom"; + this.ribbonContextMenuShowAbove = "Pokaži orodno vrstico za hitri dostop nad trakom"; + this.ribbonContextMenuCustomizeRibbon = "Prilagodi trak..."; + this.ribbonContextMenuMinimizeRibbon = "Minimiraj trak"; + + // To see it in Word: open *.doc (not *.docx) and see Insert->Screenshot + // (This prop was introduced after v1.3) + this.screenTipDisableReasonHeader = "Ta ukaz je trenutno onemogočen."; + + // Right-click on status bar to see it. NEW! from v2.0 + this.customizeStatusBar = "Prilagodi vrstico stanja"; + } + + #endregion + + #region Catalan + + private void LoadCatalan() + { + // Backstage button text & key tip + this.backstageButtonText = "Fitxer"; + this.backstageButtonKeyTip = "F"; + + // See right-top corner... (two different tooltips must be if you press it) + // TRANSLATOR'S NOTE: This block is not shown at Windows 7's Apps (WordPad or Paint) + this.minimizeButtonScreenTipTitle = "Minimitza la cinta (Ctrl + F1)"; + this.minimizeButtonScreenTipText = "Ensenya o amaga la cinta\n\nQuan la cinta no es mostri, només s'ensenyen els noms de les pestanyes"; + this.expandButtonScreenTipTitle = "Expandeix la cinta (Ctrl + F1)"; + this.expandButtonScreenTipText = "Ensenya o amaga la cinta\n\nQuan la cinta no es mostri, només s'ensenyen els noms de les pestanyes"; + + // QAT tooltips and menu items + this.quickAccessToolBarDropDownButtonTooltip = "Personalitza la barra d'eines d'accés ràpid"; + this.quickAccessToolBarMoreControlsButtonTooltip = "Més controls"; // When two arrows appear ">>" + this.quickAccessToolBarMenuHeader = "Personalitza la barra d'eines d'accés ràpid"; + this.quickAccessToolBarMenuShowAbove = "Mostra sobre la cinta"; + this.quickAccessToolBarMenuShowBelow = "Mostra sota la cinta"; + + // Click on Ribbon to show context menu + this.ribbonContextMenuAddItem = "Afegeix a la barra d'eines d'accés ràpid"; // Button + this.ribbonContextMenuAddGroup = "Afegeix grup a la barra d'eines d'accés ràpid"; // For ex., by collapsed group + this.ribbonContextMenuAddGallery = "Afegeix galeria a la barra d'eines d'accés ràpid"; // For ex., by opened font context menu + this.ribbonContextMenuAddMenu = "Afegeix menú a la barra d'eines d'accés ràpid"; // By dashed splitter in context menu + this.ribbonContextMenuRemoveItem = "Elimina la barra d'eines d'accés ràpid"; // By item in QAT + this.ribbonContextMenuCustomizeQuickAccessToolbar = "Personalitza la barra d'eines d'accés ràpid..."; + this.ribbonContextMenuShowBelow = "Mostra la barra d'eines d'accés ràpid sota la cinta"; + this.ribbonContextMenuShowAbove = "Mostra la barra d'eines d'accés ràpid sobre la cinta"; + this.ribbonContextMenuCustomizeRibbon = "Personalitza la cinta..."; + this.ribbonContextMenuMinimizeRibbon = "Minimitza la cinta"; + } + + #endregion + + #region Estonian + + private void LoadEstonian() + { + // Backstage button text & key tip + this.backstageButtonText = "Fail"; + this.backstageButtonKeyTip = "F"; + + // See right-top corner... (two different tooltips must be if you press it) + this.minimizeButtonScreenTipTitle = "Ahenda menüülint (Ctrl+F1)"; + this.minimizeButtonScreenTipText = "Kas vajate rohkem ruumi? Ahendage lint, siis kuvatakse \nainult menüünimed."; + this.expandButtonScreenTipTitle = "Kinnita lint (Ctrl+F1)"; + this.expandButtonScreenTipText = "Kas soovite, et lint oleks kuvatud? Saate selle töötamise \najal avatuna hoida."; + + // QAT tooltips and menu items + this.quickAccessToolBarDropDownButtonTooltip = "Kohanda kiirpääsuriba"; + this.quickAccessToolBarMoreControlsButtonTooltip = "Rohkem juhtelemente"; // When two arrows appear ">>" + this.quickAccessToolBarMenuHeader = "Kohanda kiirpääsuriba"; + this.quickAccessToolBarMenuShowAbove = "Kuva lindi kohal"; + this.quickAccessToolBarMenuShowBelow = "Kuva lindi all"; + + // Click on Ribbon to show context menu + this.ribbonContextMenuAddItem = "Lisa kiirpääsuribale"; // Button + this.ribbonContextMenuAddGroup = "Lisa rühm kiirpääsuribale"; // For ex., by collapsed group + this.ribbonContextMenuAddGallery = "Lisa galerii kiirpääsuribale"; // For ex., by opened font context menu + this.ribbonContextMenuAddMenu = "Lisa menüü kiirpääsuribale"; // By dashed splitter in context menu + this.ribbonContextMenuRemoveItem = "Eemalda kiirpääsuribalt"; // By item in QAT + this.ribbonContextMenuCustomizeQuickAccessToolbar = "Kohanda kiirpääsuriba..."; + this.ribbonContextMenuShowBelow = "Kuva kiirpääsuriba lindi all"; + this.ribbonContextMenuShowAbove = "Kuva kiirpääsuriba lindi kohal"; + this.ribbonContextMenuCustomizeRibbon = "Kohanda linti..."; + this.ribbonContextMenuMinimizeRibbon = "Ahenda menüülint"; + + // To see it in Word: open *.doc (not *.docx) and see Insert->Screenshot + // (This prop was introduced after v1.3) + //Screentips + this.screenTipDisableReasonHeader = "See käsk on praegu keelatud."; + this.screenTipF1LabelHeader = "Spikri kuvamiseks vajutage klahvi F1"; + + // Right-click on status bar to see it. NEW! from v2.0 + this.customizeStatusBar = "Kohanda olekuriba"; + } + + #endregion + + //Add by gamegear.tw + //Note: Adding Tradchinese + #region TradChinese + private void LoadTradChinese() + { + // Backstage button text & key tip + this.backstageButtonText = "檔案"; + this.backstageButtonKeyTip = "F"; + + // See right-top corner... (two different tooltips must be if you press it) + this.minimizeButtonScreenTipTitle = "功能區最小化 (Ctrl + F1)"; + this.minimizeButtonScreenTipText = "僅顯示功能區上的選項名稱,點擊選項後可顯示命令。"; + this.expandButtonScreenTipTitle = "展開功能區 (Ctrl + F1)"; + this.expandButtonScreenTipText = "始終顯示功能區選項及命令。"; + + // QAT tooltips and menu items + this.quickAccessToolBarDropDownButtonTooltip = "自訂快速存取工具列"; + this.quickAccessToolBarMoreControlsButtonTooltip = "其他命令"; // When two arrows appear ">>" + this.quickAccessToolBarMenuHeader = "自訂快速存取工具列"; + this.quickAccessToolBarMenuShowAbove = "在功能區上方顯示"; + this.quickAccessToolBarMenuShowBelow = "在功能區下方顯示"; + + // Click on Ribbon to show context menu + this.ribbonContextMenuAddItem = "新增到快速存取工具列"; // Button + this.ribbonContextMenuAddGroup = "將群組新增到快速存取工具列"; // For ex., by collapsed group + this.ribbonContextMenuAddGallery = "將樣式新增到快速存取工具列"; // For ex., by opened font context menu + this.ribbonContextMenuAddMenu = "將選單新增到快速存取工具列"; // By dashed splitter in context menu + this.ribbonContextMenuRemoveItem = "將選單從快速存取工具列中移除"; // By item in QAT + this.ribbonContextMenuCustomizeQuickAccessToolbar = "自訂快速存取工具列..."; + this.ribbonContextMenuShowBelow = "在功能表下方顯示快速存取工具列"; + this.ribbonContextMenuShowAbove = "在功能表上方顯示快速存取工具列"; + this.ribbonContextMenuCustomizeRibbon = "自訂功能區..."; + this.ribbonContextMenuMinimizeRibbon = "功能區最小化"; + + // To see it in Word: open *.doc (not *.docx) and see Insert->Screenshot + // (This prop was introduced after v1.3) + this.screenTipDisableReasonHeader = "命令已被禁用。"; + + // Right-click on status bar to see it. NEW! from v2.0 + this.customizeStatusBar = "自訂狀態工具列"; + } + #endregion + + + } +} diff --git a/Fluent/RibbonToolBarTray.cs b/Fluent.Ribbon/RibbonToolBarTray.cs similarity index 97% rename from Fluent/RibbonToolBarTray.cs rename to Fluent.Ribbon/RibbonToolBarTray.cs index ed620233d..09f8130be 100644 --- a/Fluent/RibbonToolBarTray.cs +++ b/Fluent.Ribbon/RibbonToolBarTray.cs @@ -1,403 +1,403 @@ -#region Copyright and License Information -// Fluent Ribbon Control Suite -// http://fluent.codeplex.com/ -// Copyright � Degtyarev Daniel, Rikker Serg. 2009-2010. All rights reserved. -// -// Distributed under the terms of the Microsoft Public License (Ms-PL). -// The license is available online http://fluent.codeplex.com/license -#endregion -using System; -using System.Windows; -using System.Windows.Markup; -using System.Windows.Controls; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Fluent -{ - /// - /// Represents panel to layout ribbon controls in toolbar style - /// - [ContentProperty("Children")] - public class RibbonToolBarTray : Panel - { - #region Fields - - int[][] condensedOrder; - - #endregion - - #region Properties - - #region Condensed Order - - /// - /// Gets or sets order of elements in condensed state - /// (for example, 0,1,2;3,4;5,6) - /// - public string CondensedOrder - { - get { return (string)GetValue(CondensedOrderProperty); } - set { SetValue(CondensedOrderProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for CondensedOrder. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty CondensedOrderProperty = - DependencyProperty.Register("CondensedOrder", typeof(string), typeof(RibbonToolBarTray), new FrameworkPropertyMetadata(null, - FrameworkPropertyMetadataOptions.AffectsMeasure | - FrameworkPropertyMetadataOptions.AffectsArrange | - FrameworkPropertyMetadataOptions.AffectsRender, OnCondensedOrderPropertyChanged)); - - static void OnCondensedOrderPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - RibbonToolBarTray tray = (RibbonToolBarTray)d; - string[] rows = ((string)e.NewValue).Split(';'); - tray.condensedOrder = new int[3][]; - bool errorOccured = false; - for (int i = 0; i < 3; i++) - { - if (i + 1 > rows.Length) { tray.condensedOrder[i] = new int[0]; continue; } - tray.condensedOrder[i] = rows[i].Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries) - .Select(x => { - int result = 0; - errorOccured = errorOccured || !Int32.TryParse(x, out result); - return result; }).ToArray(); - } - System.Diagnostics.Debug.WriteLineIf(errorOccured, "The property CondensedOrder has incorrect formatted value " + e.NewValue); - } - - #endregion - - #region IsCondensed - - // TODO: add behavior to toolbar tray corresponds current size of the ribbon groupbox - - /// - /// Gets or sets whether the tray is condensed state - /// (i.e. three lines layout) - /// - public bool IsCondensed - { - get { return (bool)GetValue(IsCondensedProperty); } - set { SetValue(IsCondensedProperty, value); } - } - - /// - /// Using a DependencyProperty as the backing store for IsCondensed. - /// This enables animation, styling, binding, etc... - /// - public static readonly DependencyProperty IsCondensedProperty = - DependencyProperty.Register("IsCondensed", typeof(bool), typeof(RibbonToolBarTray), new FrameworkPropertyMetadata(false, - FrameworkPropertyMetadataOptions.AffectsMeasure | - FrameworkPropertyMetadataOptions.AffectsArrange | - FrameworkPropertyMetadataOptions.AffectsRender)); - - - #endregion - - #endregion - - #region Initialization - - /// - /// Default constructor - /// - public RibbonToolBarTray() - { - - } - - #endregion - - #region Layout - - /// - /// When overridden in a derived class, measures the size - /// in layout required for child elements and determines a - /// size for the System.Windows.FrameworkElement-derived class. - /// - /// The available size that this element can give to child elements - /// The size that this element determines - protected override Size MeasureOverride(Size availableSize) - { - MeasureChildren(); - - - if (IsCondensed && CondensedOrder == null) - { - // if condensed order is not present, we make automatic order - condensedOrder = new int[3][] { new int[0], new int[0], new int[0] }; - - double totalWidth = GetChildrenWidth(); - double threshold = totalWidth / 3.0; - - double x = 0; - bool hasStretchable = false; - int nextStartIndex = 0; - int currentRow = 0; - for (int i = 0; i < Children.Count; i++) - { - Size desiredSize = Children[i].DesiredSize; - if (x + desiredSize.Width > threshold) - { - // Break to next row - x = 0; - - // If the current row has a stretchable control - // we split before the current control to make the next row larger - if (hasStretchable) - { - hasStretchable = false; - condensedOrder[currentRow] = MakeArray(nextStartIndex, i - 1); - nextStartIndex = i; - currentRow++; - if (currentRow == 2) - { - condensedOrder[currentRow] = MakeArray(nextStartIndex, Children.Count - 1); - break; - } - } - else - { - condensedOrder[currentRow] = MakeArray(nextStartIndex, i); - nextStartIndex = i + 1; - currentRow++; - if (currentRow == 2) - { - condensedOrder[currentRow] = MakeArray(nextStartIndex, Children.Count - 1); - break; - } - continue; - } - } - - x += desiredSize.Width; - if ((Children[i] as FrameworkElement).HorizontalAlignment == HorizontalAlignment.Stretch) - hasStretchable = true; - } - - } - - if (IsCondensed) - { - double maxWidth; - double[] widths; - CalculateCondensedWidths(condensedOrder, out maxWidth, out widths); - return new Size(maxWidth, Double.IsPositiveInfinity(availableSize.Height) ? 0 : availableSize.Height); - } - else - { - int breakIndex; - double firstPartWidth; - double maxWidth; - FindWhereWeCanSplitUncondensed(out breakIndex, out firstPartWidth, out maxWidth); - - return new Size(maxWidth, Double.IsPositiveInfinity(availableSize.Height) ? 0 : availableSize.Height); - } - } - - // Summ all width of all children - double GetChildrenWidth() - { - double width = 0; - foreach (UIElement element in InternalChildren) width += element.DesiredSize.Width; - return width; - } - - // Measures children - void MeasureChildren() - { - Size size = new Size(Double.PositiveInfinity, Double.PositiveInfinity); - foreach (FrameworkElement element in Children) - { - if (element.HorizontalAlignment == HorizontalAlignment.Stretch) - element.Measure(new Size(element.MinWidth, Double.PositiveInfinity)); - else element.Measure(size); - } - } - - /// - /// When overridden in a derived class, positions child elements and determines - /// a size for a System.Windows.FrameworkElement derived class - /// - /// The final area within the parent that this - /// element should use to arrange itself and its children - /// The actual size used - protected override Size ArrangeOverride(Size finalSize) - { - if (Children.Count == 0) return finalSize; - - return IsCondensed ? - ArrangeCondensed(condensedOrder, finalSize) : - ArrangeUncondensed(finalSize); - } - - static int[] MakeArray(int from, int to) - { - int[] array = new int[to - from + 1]; - for (int i = from; i <= to; i++) - { - array[i - from] = i; - } - return array; - } - - Size ArrangeUncondensed(Size finalSize) - { - if (Children.Count == 0) return finalSize; - - double childrenHeight = Children[0].DesiredSize.Height; - double totalWidth = GetChildrenWidth(); - - int breakIndex; - double firstPartWidth; - double maxWidth; - FindWhereWeCanSplitUncondensed(out breakIndex, out firstPartWidth, out maxWidth); - - - // Arranging - double space = (finalSize.Height - (childrenHeight * 2.0)) / 3.0; - double y = space; - double x = 0; - bool stillSeekStretching = true; - for (int i = 0; i < Children.Count; i++) - { - if (i == breakIndex) - { - x = 0; - stillSeekStretching = true; - y += space + childrenHeight; - } - - FrameworkElement element = Children[i] as FrameworkElement; - if (element == null) continue; - if (stillSeekStretching && element.HorizontalAlignment == HorizontalAlignment.Stretch) - { - stillSeekStretching = false; - double w = maxWidth - (i >= breakIndex ? totalWidth - firstPartWidth : firstPartWidth) + element.DesiredSize.Width; - element.Arrange(new Rect(x, y, w, childrenHeight)); - x += w; - } - else - { - element.Arrange(new Rect(x, y, element.DesiredSize.Width, childrenHeight)); - x += element.DesiredSize.Width; - } - - } - - return finalSize; - } - - // Calculates where we can split and widths - void FindWhereWeCanSplitUncondensed(out int breakIndex, out double firstPartWidth, out double maxWidth) - { - double totalWidth = GetChildrenWidth(); - double threshold = totalWidth / 2.0; - - // Find where we can split/ break - double x = 0; - bool hasStretchable = false; - breakIndex = 0; - firstPartWidth = 0; - for (int i = 0; i < Children.Count; i++) - { - Size desiredSize = Children[i].DesiredSize; - if ((x + desiredSize.Width > threshold) && (i != 0)) - { - if (hasStretchable) - { - breakIndex = i; - firstPartWidth = x; - } - else - { - breakIndex = i + 1; - firstPartWidth = x + desiredSize.Width; - } - break; - } - - x += desiredSize.Width; - if ((Children[i] as FrameworkElement).HorizontalAlignment == HorizontalAlignment.Stretch) - hasStretchable = true; - } - maxWidth = Math.Max(firstPartWidth, totalWidth - firstPartWidth); - } - - // Arranges children using the given condensedOrder - Size ArrangeCondensed(int[][] order, Size finalSize) - { - if (Children.Count == 0) return finalSize; - double childrenHeight = Children[0].DesiredSize.Height; - - double maxWidth; - double[] widths; - CalculateCondensedWidths(order, out maxWidth, out widths); - - // Removes skipped items - if (order[0].Length + order[1].Length + order[2].Length < Children.Count) - { - // TODO: fix this dirty way to hide skipped controls in toolbar tray - Rect empty = new Rect(-10000, -10000, 0.01, 0.01); - foreach (UIElement item in Children) item.Arrange(empty); - } - - // Arranging - double space = (finalSize.Height - (childrenHeight * 3.0)) / 4.0; - double y = space; - for (int i = 0; i < 3; i++) - { - double x = 0; - bool stillSeekStretching = true; - for (int j = 0; j < order[i].Length; j++) - { - FrameworkElement element = Children[order[i][j]] as FrameworkElement; - if (element == null) continue; - if (stillSeekStretching && element.HorizontalAlignment == HorizontalAlignment.Stretch) - { - // We have found a stretchable item. - // Let the stretchable item take all - // available space in the current row - stillSeekStretching = false; - double w = maxWidth - widths[i] + element.DesiredSize.Width; - element.Arrange(new Rect(x, y, w, childrenHeight)); - x += w; - } - else - { - element.Arrange(new Rect(x, y, element.DesiredSize.Width, childrenHeight)); - x += element.DesiredSize.Width; - } - } - y += childrenHeight + space; - } - - return new Size(maxWidth, finalSize.Height); - } - - void CalculateCondensedWidths(int[][] order, out double maxWidth, out double[] widths) - { - // Calculate max width - maxWidth = 0; - widths = new double[3]; - for (int i = 0; i < 3; i++) - { - // Calculate max width - double width = 0; - for (int j = 0; j < order[i].Length; j++) - { - width += Children[order[i][j]].DesiredSize.Width; - } - widths[i] = width; - maxWidth = Math.Max(maxWidth, width); - } - } - - #endregion - } -} +#region Copyright and License Information +// Fluent Ribbon Control Suite +// http://fluent.codeplex.com/ +// Copyright � Degtyarev Daniel, Rikker Serg. 2009-2010. All rights reserved. +// +// Distributed under the terms of the Microsoft Public License (Ms-PL). +// The license is available online http://fluent.codeplex.com/license +#endregion +using System; +using System.Windows; +using System.Windows.Markup; +using System.Windows.Controls; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Fluent +{ + /// + /// Represents panel to layout ribbon controls in toolbar style + /// + [ContentProperty("Children")] + public class RibbonToolBarTray : Panel + { + #region Fields + + int[][] condensedOrder; + + #endregion + + #region Properties + + #region Condensed Order + + /// + /// Gets or sets order of elements in condensed state + /// (for example, 0,1,2;3,4;5,6) + /// + public string CondensedOrder + { + get { return (string)GetValue(CondensedOrderProperty); } + set { SetValue(CondensedOrderProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for CondensedOrder. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty CondensedOrderProperty = + DependencyProperty.Register("CondensedOrder", typeof(string), typeof(RibbonToolBarTray), new FrameworkPropertyMetadata(null, + FrameworkPropertyMetadataOptions.AffectsMeasure | + FrameworkPropertyMetadataOptions.AffectsArrange | + FrameworkPropertyMetadataOptions.AffectsRender, OnCondensedOrderPropertyChanged)); + + static void OnCondensedOrderPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + RibbonToolBarTray tray = (RibbonToolBarTray)d; + string[] rows = ((string)e.NewValue).Split(';'); + tray.condensedOrder = new int[3][]; + bool errorOccured = false; + for (int i = 0; i < 3; i++) + { + if (i + 1 > rows.Length) { tray.condensedOrder[i] = new int[0]; continue; } + tray.condensedOrder[i] = rows[i].Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries) + .Select(x => { + int result = 0; + errorOccured = errorOccured || !Int32.TryParse(x, out result); + return result; }).ToArray(); + } + System.Diagnostics.Debug.WriteLineIf(errorOccured, "The property CondensedOrder has incorrect formatted value " + e.NewValue); + } + + #endregion + + #region IsCondensed + + // TODO: add behavior to toolbar tray corresponds current size of the ribbon groupbox + + /// + /// Gets or sets whether the tray is condensed state + /// (i.e. three lines layout) + /// + public bool IsCondensed + { + get { return (bool)GetValue(IsCondensedProperty); } + set { SetValue(IsCondensedProperty, value); } + } + + /// + /// Using a DependencyProperty as the backing store for IsCondensed. + /// This enables animation, styling, binding, etc... + /// + public static readonly DependencyProperty IsCondensedProperty = + DependencyProperty.Register("IsCondensed", typeof(bool), typeof(RibbonToolBarTray), new FrameworkPropertyMetadata(false, + FrameworkPropertyMetadataOptions.AffectsMeasure | + FrameworkPropertyMetadataOptions.AffectsArrange | + FrameworkPropertyMetadataOptions.AffectsRender)); + + + #endregion + + #endregion + + #region Initialization + + /// + /// Default constructor + /// + public RibbonToolBarTray() + { + + } + + #endregion + + #region Layout + + /// + /// When overridden in a derived class, measures the size + /// in layout required for child elements and determines a + /// size for the System.Windows.FrameworkElement-derived class. + /// + /// The available size that this element can give to child elements + /// The size that this element determines + protected override Size MeasureOverride(Size availableSize) + { + MeasureChildren(); + + + if (IsCondensed && CondensedOrder == null) + { + // if condensed order is not present, we make automatic order + condensedOrder = new int[3][] { new int[0], new int[0], new int[0] }; + + double totalWidth = GetChildrenWidth(); + double threshold = totalWidth / 3.0; + + double x = 0; + bool hasStretchable = false; + int nextStartIndex = 0; + int currentRow = 0; + for (int i = 0; i < Children.Count; i++) + { + Size desiredSize = Children[i].DesiredSize; + if (x + desiredSize.Width > threshold) + { + // Break to next row + x = 0; + + // If the current row has a stretchable control + // we split before the current control to make the next row larger + if (hasStretchable) + { + hasStretchable = false; + condensedOrder[currentRow] = MakeArray(nextStartIndex, i - 1); + nextStartIndex = i; + currentRow++; + if (currentRow == 2) + { + condensedOrder[currentRow] = MakeArray(nextStartIndex, Children.Count - 1); + break; + } + } + else + { + condensedOrder[currentRow] = MakeArray(nextStartIndex, i); + nextStartIndex = i + 1; + currentRow++; + if (currentRow == 2) + { + condensedOrder[currentRow] = MakeArray(nextStartIndex, Children.Count - 1); + break; + } + continue; + } + } + + x += desiredSize.Width; + if ((Children[i] as FrameworkElement).HorizontalAlignment == HorizontalAlignment.Stretch) + hasStretchable = true; + } + + } + + if (IsCondensed) + { + double maxWidth; + double[] widths; + CalculateCondensedWidths(condensedOrder, out maxWidth, out widths); + return new Size(maxWidth, Double.IsPositiveInfinity(availableSize.Height) ? 0 : availableSize.Height); + } + else + { + int breakIndex; + double firstPartWidth; + double maxWidth; + FindWhereWeCanSplitUncondensed(out breakIndex, out firstPartWidth, out maxWidth); + + return new Size(maxWidth, Double.IsPositiveInfinity(availableSize.Height) ? 0 : availableSize.Height); + } + } + + // Summ all width of all children + double GetChildrenWidth() + { + double width = 0; + foreach (UIElement element in InternalChildren) width += element.DesiredSize.Width; + return width; + } + + // Measures children + void MeasureChildren() + { + Size size = new Size(Double.PositiveInfinity, Double.PositiveInfinity); + foreach (FrameworkElement element in Children) + { + if (element.HorizontalAlignment == HorizontalAlignment.Stretch) + element.Measure(new Size(element.MinWidth, Double.PositiveInfinity)); + else element.Measure(size); + } + } + + /// + /// When overridden in a derived class, positions child elements and determines + /// a size for a System.Windows.FrameworkElement derived class + /// + /// The final area within the parent that this + /// element should use to arrange itself and its children + /// The actual size used + protected override Size ArrangeOverride(Size finalSize) + { + if (Children.Count == 0) return finalSize; + + return IsCondensed ? + ArrangeCondensed(condensedOrder, finalSize) : + ArrangeUncondensed(finalSize); + } + + static int[] MakeArray(int from, int to) + { + int[] array = new int[to - from + 1]; + for (int i = from; i <= to; i++) + { + array[i - from] = i; + } + return array; + } + + Size ArrangeUncondensed(Size finalSize) + { + if (Children.Count == 0) return finalSize; + + double childrenHeight = Children[0].DesiredSize.Height; + double totalWidth = GetChildrenWidth(); + + int breakIndex; + double firstPartWidth; + double maxWidth; + FindWhereWeCanSplitUncondensed(out breakIndex, out firstPartWidth, out maxWidth); + + + // Arranging + double space = (finalSize.Height - (childrenHeight * 2.0)) / 3.0; + double y = space; + double x = 0; + bool stillSeekStretching = true; + for (int i = 0; i < Children.Count; i++) + { + if (i == breakIndex) + { + x = 0; + stillSeekStretching = true; + y += space + childrenHeight; + } + + FrameworkElement element = Children[i] as FrameworkElement; + if (element == null) continue; + if (stillSeekStretching && element.HorizontalAlignment == HorizontalAlignment.Stretch) + { + stillSeekStretching = false; + double w = maxWidth - (i >= breakIndex ? totalWidth - firstPartWidth : firstPartWidth) + element.DesiredSize.Width; + element.Arrange(new Rect(x, y, w, childrenHeight)); + x += w; + } + else + { + element.Arrange(new Rect(x, y, element.DesiredSize.Width, childrenHeight)); + x += element.DesiredSize.Width; + } + + } + + return finalSize; + } + + // Calculates where we can split and widths + void FindWhereWeCanSplitUncondensed(out int breakIndex, out double firstPartWidth, out double maxWidth) + { + double totalWidth = GetChildrenWidth(); + double threshold = totalWidth / 2.0; + + // Find where we can split/ break + double x = 0; + bool hasStretchable = false; + breakIndex = 0; + firstPartWidth = 0; + for (int i = 0; i < Children.Count; i++) + { + Size desiredSize = Children[i].DesiredSize; + if ((x + desiredSize.Width > threshold) && (i != 0)) + { + if (hasStretchable) + { + breakIndex = i; + firstPartWidth = x; + } + else + { + breakIndex = i + 1; + firstPartWidth = x + desiredSize.Width; + } + break; + } + + x += desiredSize.Width; + if ((Children[i] as FrameworkElement).HorizontalAlignment == HorizontalAlignment.Stretch) + hasStretchable = true; + } + maxWidth = Math.Max(firstPartWidth, totalWidth - firstPartWidth); + } + + // Arranges children using the given condensedOrder + Size ArrangeCondensed(int[][] order, Size finalSize) + { + if (Children.Count == 0) return finalSize; + double childrenHeight = Children[0].DesiredSize.Height; + + double maxWidth; + double[] widths; + CalculateCondensedWidths(order, out maxWidth, out widths); + + // Removes skipped items + if (order[0].Length + order[1].Length + order[2].Length < Children.Count) + { + // TODO: fix this dirty way to hide skipped controls in toolbar tray + Rect empty = new Rect(-10000, -10000, 0.01, 0.01); + foreach (UIElement item in Children) item.Arrange(empty); + } + + // Arranging + double space = (finalSize.Height - (childrenHeight * 3.0)) / 4.0; + double y = space; + for (int i = 0; i < 3; i++) + { + double x = 0; + bool stillSeekStretching = true; + for (int j = 0; j < order[i].Length; j++) + { + FrameworkElement element = Children[order[i][j]] as FrameworkElement; + if (element == null) continue; + if (stillSeekStretching && element.HorizontalAlignment == HorizontalAlignment.Stretch) + { + // We have found a stretchable item. + // Let the stretchable item take all + // available space in the current row + stillSeekStretching = false; + double w = maxWidth - widths[i] + element.DesiredSize.Width; + element.Arrange(new Rect(x, y, w, childrenHeight)); + x += w; + } + else + { + element.Arrange(new Rect(x, y, element.DesiredSize.Width, childrenHeight)); + x += element.DesiredSize.Width; + } + } + y += childrenHeight + space; + } + + return new Size(maxWidth, finalSize.Height); + } + + void CalculateCondensedWidths(int[][] order, out double maxWidth, out double[] widths) + { + // Calculate max width + maxWidth = 0; + widths = new double[3]; + for (int i = 0; i < 3; i++) + { + // Calculate max width + double width = 0; + for (int j = 0; j < order[i].Length; j++) + { + width += Children[order[i][j]].DesiredSize.Width; + } + widths[i] = width; + maxWidth = Math.Max(maxWidth, width); + } + } + + #endregion + } +} diff --git a/Fluent/Services/ContextMenuService.cs b/Fluent.Ribbon/Services/ContextMenuService.cs similarity index 97% rename from Fluent/Services/ContextMenuService.cs rename to Fluent.Ribbon/Services/ContextMenuService.cs index 696a20691..cd7659486 100644 --- a/Fluent/Services/ContextMenuService.cs +++ b/Fluent.Ribbon/Services/ContextMenuService.cs @@ -1,47 +1,47 @@ -using System; -using System.Windows; - -namespace Fluent -{ - /// - /// Represents additional context menu service - /// - public static class ContextMenuService - { - /// - /// Attach needed parameters to control - /// - /// - public static void Attach(Type type) - { - System.Windows.Controls.ContextMenuService.ShowOnDisabledProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(true)); - FrameworkElement.ContextMenuProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(null, OnContextMenuChanged, CoerceContextMenu)); - } - - private static void OnContextMenuChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - d.CoerceValue(FrameworkElement.ContextMenuProperty); - } - - private static object CoerceContextMenu(DependencyObject d, object basevalue) - { - var control = d as IQuickAccessItemProvider; - if (basevalue == null - && (control == null || control.CanAddToQuickAccessToolBar)) - { - return Ribbon.RibbonContextMenu; - } - - return basevalue; - } - - /// - /// Coerce control context menu - /// - /// Control - public static void Coerce(DependencyObject o) - { - o.CoerceValue(FrameworkElement.ContextMenuProperty); - } - } +using System; +using System.Windows; + +namespace Fluent +{ + /// + /// Represents additional context menu service + /// + public static class ContextMenuService + { + /// + /// Attach needed parameters to control + /// + /// + public static void Attach(Type type) + { + System.Windows.Controls.ContextMenuService.ShowOnDisabledProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(true)); + FrameworkElement.ContextMenuProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(null, OnContextMenuChanged, CoerceContextMenu)); + } + + private static void OnContextMenuChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + d.CoerceValue(FrameworkElement.ContextMenuProperty); + } + + private static object CoerceContextMenu(DependencyObject d, object basevalue) + { + var control = d as IQuickAccessItemProvider; + if (basevalue == null + && (control == null || control.CanAddToQuickAccessToolBar)) + { + return Ribbon.RibbonContextMenu; + } + + return basevalue; + } + + /// + /// Coerce control context menu + /// + /// Control + public static void Coerce(DependencyObject o) + { + o.CoerceValue(FrameworkElement.ContextMenuProperty); + } + } } \ No newline at end of file diff --git a/Fluent/Services/KeyTipService.cs b/Fluent.Ribbon/Services/KeyTipService.cs similarity index 96% rename from Fluent/Services/KeyTipService.cs rename to Fluent.Ribbon/Services/KeyTipService.cs index 91a87d753..1eaf5d749 100644 --- a/Fluent/Services/KeyTipService.cs +++ b/Fluent.Ribbon/Services/KeyTipService.cs @@ -1,419 +1,419 @@ -namespace Fluent -{ - using System; - using System.ComponentModel; - using System.Windows; - using System.Windows.Input; - using System.Windows.Interop; - using System.Windows.Threading; - using Fluent.Internal; - using Fluent.Metro.Native; - - /// - /// Handles Alt, F10 and so on - /// - internal class KeyTipService - { - #region Fields - - // Host element, usually this is Ribbon - private readonly Ribbon ribbon; - - // Timer to show KeyTips with delay - private readonly DispatcherTimer timer; - - // Is KeyTips Actived now - private KeyTipAdorner activeAdornerChain; - // This element must be remembered to restore it - IInputElement backUpFocusedElement; - // Window where we attached - private Window window; - - // Whether we attached to window - private bool attached; - - // Attached HWND source - private HwndSource attachedHwndSource; - - private static readonly KeyConverter keyConverter = new KeyConverter(); - private string currentUserInput; - - /// - /// Checks if any keytips are visible. - /// - public bool AreAnyKeyTipsVisible - { - get - { - if (this.activeAdornerChain != null) - { - return this.activeAdornerChain.AreAnyKeyTipsVisible; - } - - return false; - } - } - - #endregion - - #region Initialization - - /// - /// Default constrctor - /// - /// Host element - public KeyTipService(Ribbon ribbon) - { - this.ribbon = ribbon; - - if (this.ribbon.IsLoaded == false) - { - this.ribbon.Loaded += this.OnDelayedInitialization; - } - else - { - this.Attach(); - } - - // Initialize timer - this.timer = new DispatcherTimer(TimeSpan.FromSeconds(0.7), DispatcherPriority.SystemIdle, this.OnDelayedShow, Dispatcher.CurrentDispatcher); - this.timer.Stop(); - } - - private void OnDelayedInitialization(object sender, EventArgs args) - { - this.ribbon.Loaded -= this.OnDelayedInitialization; - this.Attach(); - } - - #endregion - - /// - /// Attaches self - /// - public void Attach() - { - if (this.attached) - { - return; - } - - this.attached = true; - - // KeyTip service must not work in design mode - if (DesignerProperties.GetIsInDesignMode(this.ribbon)) - { - return; - } - - this.window = Window.GetWindow(this.ribbon); - if (this.window == null) - { - return; - } - - this.window.PreviewKeyDown += this.OnWindowKeyDown; - this.window.KeyUp += this.OnWindowKeyUp; - - // Hookup non client area messages - this.attachedHwndSource = (HwndSource)PresentationSource.FromVisual(this.window); - if (this.attachedHwndSource != null) - { - this.attachedHwndSource.AddHook(this.WindowProc); - } - } - - /// - /// Detachs self - /// - public void Detach() - { - if (this.attached == false) - { - return; - } - - this.attached = false; - - // prevent delay show - this.timer.Stop(); - - if (this.window != null) - { - this.window.PreviewKeyDown -= this.OnWindowKeyDown; - this.window.KeyUp -= this.OnWindowKeyUp; - - this.window = null; - } - - // Hookup non client area messages - if (this.attachedHwndSource != null - && this.attachedHwndSource.IsDisposed == false) - { - this.attachedHwndSource.RemoveHook(this.WindowProc); - } - } - - // Window's messages hook up - private IntPtr WindowProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) - { - // We must terminate the keytip's adorner chain if: - // - mouse clicks in non client area - // - the window is deactivated - if (((msg >= 161) && (msg <= 173)) || msg == Constants.WM_NCACTIVATE) - { - if (this.activeAdornerChain != null - && this.activeAdornerChain.IsAdornerChainAlive) - { - this.activeAdornerChain.Terminate(); - this.activeAdornerChain = null; - } - } - - return IntPtr.Zero; - } - - private void OnWindowKeyDown(object sender, KeyEventArgs e) - { - if (e.IsRepeat) - { - return; - } - - if (this.ribbon.IsCollapsed) - { - return; - } - - if (IsShowOrHideKey(e)) - { - if (this.activeAdornerChain == null - || this.activeAdornerChain.IsAdornerChainAlive == false - || this.activeAdornerChain.AreAnyKeyTipsVisible == false) - { - this.ShowDelayed(); - } - else if (this.activeAdornerChain != null - && this.activeAdornerChain.IsAdornerChainAlive) - { - // Focus ribbon - this.backUpFocusedElement = Keyboard.FocusedElement; - this.ribbon.Focusable = true; - //this.ribbon.Focus(); - - this.activeAdornerChain.Terminate(); - this.activeAdornerChain = null; - } - else - { - this.ClearUserInput(); - } - } - else if (e.Key == Key.Escape - && this.activeAdornerChain != null) - { - this.activeAdornerChain.ActiveKeyTipAdorner.Back(); - } - else - { - // Should we show the keytips and immediately react to key? - if (e.Key != Key.System - || e.SystemKey == Key.Escape - || e.KeyboardDevice.Modifiers != ModifierKeys.Alt) - { - return; - } - - if (this.activeAdornerChain == null - || this.activeAdornerChain.IsAdornerChainAlive == false - || this.activeAdornerChain.AreAnyKeyTipsVisible == false) - { - this.ShowImmediatly(); - } - - if (this.activeAdornerChain == null) - { - return; - } - - this.currentUserInput += keyConverter.ConvertToString(e.SystemKey); - - if (this.activeAdornerChain.ActiveKeyTipAdorner.Forward(this.currentUserInput, true)) - { - this.ClearUserInput(); - e.Handled = true; - } - } - } - - private void OnWindowKeyUp(object sender, KeyEventArgs e) - { - if (this.ribbon.IsCollapsed) - { - return; - } - - if (IsShowOrHideKey(e)) - { - this.ClearUserInput(); - - e.Handled = true; - - if (this.timer.IsEnabled) - { - this.ShowImmediatly(); - } - } - else - { - this.timer.Stop(); - } - } - - private static bool IsShowOrHideKey(KeyEventArgs e) - { - return e.Key == Key.System - && (e.SystemKey == Key.LeftAlt - || e.SystemKey == Key.RightAlt - || e.SystemKey == Key.F10 - || e.SystemKey == Key.Space); - } - - private void ClearUserInput() - { - this.currentUserInput = string.Empty; - } - - private void RestoreFocuses() - { - if (this.backUpFocusedElement != null) - { - this.backUpFocusedElement.Focus(); - this.backUpFocusedElement = null; // Release the reference, so GC can work - } - - this.ribbon.Focusable = false; - } - - private void OnAdornerChainTerminated(object sender, EventArgs e) - { - this.RestoreFocuses(); - ((KeyTipAdorner)sender).Terminated -= this.OnAdornerChainTerminated; - } - - private void OnDelayedShow(object sender, EventArgs e) - { - if (this.activeAdornerChain == null) - { - this.Show(); - } - - this.timer.Stop(); - } - - private void ShowImmediatly() - { - this.timer.Stop(); - this.backUpFocusedElement = Keyboard.FocusedElement; - - // Focus ribbon - this.ribbon.Focusable = true; - //this.ribbon.Focus(); - - this.Show(); - } - - private void ShowDelayed() - { - if (this.activeAdornerChain != null) - { - this.activeAdornerChain.Terminate(); - } - - this.activeAdornerChain = null; - this.timer.Start(); - } - - private void Show() - { - // Check whether the window is - // - still present (prevents exceptions when window is closed by system commands) - // - still active (prevents keytips showing during Alt-Tab'ing) - if (this.window == null - || this.window.IsActive == false) - { - this.RestoreFocuses(); - return; - } - - this.ClearUserInput(); - - this.activeAdornerChain = new KeyTipAdorner(this.ribbon, this.ribbon, null); - this.activeAdornerChain.Terminated += this.OnAdornerChainTerminated; - - // Special behavior for backstage - var specialControl = this.GetBackstage() - ?? (DependencyObject)this.GetApplicationMenu(); - - if (specialControl != null) - { - this.DirectlyForwardToSpecialControl(specialControl); - } - else - { - this.activeAdornerChain.Attach(); - } - } - - private Backstage GetBackstage() - { - if (this.ribbon.Menu == null) - { - return null; - } - - var control = this.ribbon.Menu as Backstage ?? UIHelper.FindImmediateVisualChild(this.ribbon.Menu, obj => obj.Visibility == Visibility.Visible && obj.IsOpen); - - if (control == null) - { - return null; - } - - return control.IsOpen - ? control - : null; - } - - private ApplicationMenu GetApplicationMenu() - { - if (this.ribbon.Menu == null) - { - return null; - } - - var control = this.ribbon.Menu as ApplicationMenu ?? UIHelper.FindImmediateVisualChild(this.ribbon.Menu, obj => obj.Visibility == Visibility.Visible); - - if (control == null) - { - return null; - } - - return control.IsDropDownOpen - ? control - : null; - } - - private void DirectlyForwardToSpecialControl(DependencyObject specialControl) - { - var keys = KeyTip.GetKeys(specialControl); - if (string.IsNullOrEmpty(keys) == false) - { - this.activeAdornerChain.Forward(KeyTip.GetKeys(specialControl), false); - } - else - { - this.activeAdornerChain.Attach(); - } - } - } +namespace Fluent +{ + using System; + using System.ComponentModel; + using System.Windows; + using System.Windows.Input; + using System.Windows.Interop; + using System.Windows.Threading; + using Fluent.Internal; + using Fluent.Metro.Native; + + /// + /// Handles Alt, F10 and so on + /// + internal class KeyTipService + { + #region Fields + + // Host element, usually this is Ribbon + private readonly Ribbon ribbon; + + // Timer to show KeyTips with delay + private readonly DispatcherTimer timer; + + // Is KeyTips Actived now + private KeyTipAdorner activeAdornerChain; + // This element must be remembered to restore it + IInputElement backUpFocusedElement; + // Window where we attached + private Window window; + + // Whether we attached to window + private bool attached; + + // Attached HWND source + private HwndSource attachedHwndSource; + + private static readonly KeyConverter keyConverter = new KeyConverter(); + private string currentUserInput; + + /// + /// Checks if any keytips are visible. + /// + public bool AreAnyKeyTipsVisible + { + get + { + if (this.activeAdornerChain != null) + { + return this.activeAdornerChain.AreAnyKeyTipsVisible; + } + + return false; + } + } + + #endregion + + #region Initialization + + /// + /// Default constrctor + /// + /// Host element + public KeyTipService(Ribbon ribbon) + { + this.ribbon = ribbon; + + if (this.ribbon.IsLoaded == false) + { + this.ribbon.Loaded += this.OnDelayedInitialization; + } + else + { + this.Attach(); + } + + // Initialize timer + this.timer = new DispatcherTimer(TimeSpan.FromSeconds(0.7), DispatcherPriority.SystemIdle, this.OnDelayedShow, Dispatcher.CurrentDispatcher); + this.timer.Stop(); + } + + private void OnDelayedInitialization(object sender, EventArgs args) + { + this.ribbon.Loaded -= this.OnDelayedInitialization; + this.Attach(); + } + + #endregion + + /// + /// Attaches self + /// + public void Attach() + { + if (this.attached) + { + return; + } + + this.attached = true; + + // KeyTip service must not work in design mode + if (DesignerProperties.GetIsInDesignMode(this.ribbon)) + { + return; + } + + this.window = Window.GetWindow(this.ribbon); + if (this.window == null) + { + return; + } + + this.window.PreviewKeyDown += this.OnWindowKeyDown; + this.window.KeyUp += this.OnWindowKeyUp; + + // Hookup non client area messages + this.attachedHwndSource = (HwndSource)PresentationSource.FromVisual(this.window); + if (this.attachedHwndSource != null) + { + this.attachedHwndSource.AddHook(this.WindowProc); + } + } + + /// + /// Detachs self + /// + public void Detach() + { + if (this.attached == false) + { + return; + } + + this.attached = false; + + // prevent delay show + this.timer.Stop(); + + if (this.window != null) + { + this.window.PreviewKeyDown -= this.OnWindowKeyDown; + this.window.KeyUp -= this.OnWindowKeyUp; + + this.window = null; + } + + // Hookup non client area messages + if (this.attachedHwndSource != null + && this.attachedHwndSource.IsDisposed == false) + { + this.attachedHwndSource.RemoveHook(this.WindowProc); + } + } + + // Window's messages hook up + private IntPtr WindowProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) + { + // We must terminate the keytip's adorner chain if: + // - mouse clicks in non client area + // - the window is deactivated + if (((msg >= 161) && (msg <= 173)) || msg == Constants.WM_NCACTIVATE) + { + if (this.activeAdornerChain != null + && this.activeAdornerChain.IsAdornerChainAlive) + { + this.activeAdornerChain.Terminate(); + this.activeAdornerChain = null; + } + } + + return IntPtr.Zero; + } + + private void OnWindowKeyDown(object sender, KeyEventArgs e) + { + if (e.IsRepeat) + { + return; + } + + if (this.ribbon.IsCollapsed) + { + return; + } + + if (IsShowOrHideKey(e)) + { + if (this.activeAdornerChain == null + || this.activeAdornerChain.IsAdornerChainAlive == false + || this.activeAdornerChain.AreAnyKeyTipsVisible == false) + { + this.ShowDelayed(); + } + else if (this.activeAdornerChain != null + && this.activeAdornerChain.IsAdornerChainAlive) + { + // Focus ribbon + this.backUpFocusedElement = Keyboard.FocusedElement; + this.ribbon.Focusable = true; + //this.ribbon.Focus(); + + this.activeAdornerChain.Terminate(); + this.activeAdornerChain = null; + } + else + { + this.ClearUserInput(); + } + } + else if (e.Key == Key.Escape + && this.activeAdornerChain != null) + { + this.activeAdornerChain.ActiveKeyTipAdorner.Back(); + } + else + { + // Should we show the keytips and immediately react to key? + if (e.Key != Key.System + || e.SystemKey == Key.Escape + || e.KeyboardDevice.Modifiers != ModifierKeys.Alt) + { + return; + } + + if (this.activeAdornerChain == null + || this.activeAdornerChain.IsAdornerChainAlive == false + || this.activeAdornerChain.AreAnyKeyTipsVisible == false) + { + this.ShowImmediatly(); + } + + if (this.activeAdornerChain == null) + { + return; + } + + this.currentUserInput += keyConverter.ConvertToString(e.SystemKey); + + if (this.activeAdornerChain.ActiveKeyTipAdorner.Forward(this.currentUserInput, true)) + { + this.ClearUserInput(); + e.Handled = true; + } + } + } + + private void OnWindowKeyUp(object sender, KeyEventArgs e) + { + if (this.ribbon.IsCollapsed) + { + return; + } + + if (IsShowOrHideKey(e)) + { + this.ClearUserInput(); + + e.Handled = true; + + if (this.timer.IsEnabled) + { + this.ShowImmediatly(); + } + } + else + { + this.timer.Stop(); + } + } + + private static bool IsShowOrHideKey(KeyEventArgs e) + { + return e.Key == Key.System + && (e.SystemKey == Key.LeftAlt + || e.SystemKey == Key.RightAlt + || e.SystemKey == Key.F10 + || e.SystemKey == Key.Space); + } + + private void ClearUserInput() + { + this.currentUserInput = string.Empty; + } + + private void RestoreFocuses() + { + if (this.backUpFocusedElement != null) + { + this.backUpFocusedElement.Focus(); + this.backUpFocusedElement = null; // Release the reference, so GC can work + } + + this.ribbon.Focusable = false; + } + + private void OnAdornerChainTerminated(object sender, EventArgs e) + { + this.RestoreFocuses(); + ((KeyTipAdorner)sender).Terminated -= this.OnAdornerChainTerminated; + } + + private void OnDelayedShow(object sender, EventArgs e) + { + if (this.activeAdornerChain == null) + { + this.Show(); + } + + this.timer.Stop(); + } + + private void ShowImmediatly() + { + this.timer.Stop(); + this.backUpFocusedElement = Keyboard.FocusedElement; + + // Focus ribbon + this.ribbon.Focusable = true; + //this.ribbon.Focus(); + + this.Show(); + } + + private void ShowDelayed() + { + if (this.activeAdornerChain != null) + { + this.activeAdornerChain.Terminate(); + } + + this.activeAdornerChain = null; + this.timer.Start(); + } + + private void Show() + { + // Check whether the window is + // - still present (prevents exceptions when window is closed by system commands) + // - still active (prevents keytips showing during Alt-Tab'ing) + if (this.window == null + || this.window.IsActive == false) + { + this.RestoreFocuses(); + return; + } + + this.ClearUserInput(); + + this.activeAdornerChain = new KeyTipAdorner(this.ribbon, this.ribbon, null); + this.activeAdornerChain.Terminated += this.OnAdornerChainTerminated; + + // Special behavior for backstage + var specialControl = this.GetBackstage() + ?? (DependencyObject)this.GetApplicationMenu(); + + if (specialControl != null) + { + this.DirectlyForwardToSpecialControl(specialControl); + } + else + { + this.activeAdornerChain.Attach(); + } + } + + private Backstage GetBackstage() + { + if (this.ribbon.Menu == null) + { + return null; + } + + var control = this.ribbon.Menu as Backstage ?? UIHelper.FindImmediateVisualChild(this.ribbon.Menu, obj => obj.Visibility == Visibility.Visible && obj.IsOpen); + + if (control == null) + { + return null; + } + + return control.IsOpen + ? control + : null; + } + + private ApplicationMenu GetApplicationMenu() + { + if (this.ribbon.Menu == null) + { + return null; + } + + var control = this.ribbon.Menu as ApplicationMenu ?? UIHelper.FindImmediateVisualChild(this.ribbon.Menu, obj => obj.Visibility == Visibility.Visible); + + if (control == null) + { + return null; + } + + return control.IsDropDownOpen + ? control + : null; + } + + private void DirectlyForwardToSpecialControl(DependencyObject specialControl) + { + var keys = KeyTip.GetKeys(specialControl); + if (string.IsNullOrEmpty(keys) == false) + { + this.activeAdornerChain.Forward(KeyTip.GetKeys(specialControl), false); + } + else + { + this.activeAdornerChain.Attach(); + } + } + } } \ No newline at end of file diff --git a/Fluent/Services/PopupService.cs b/Fluent.Ribbon/Services/PopupService.cs similarity index 97% rename from Fluent/Services/PopupService.cs rename to Fluent.Ribbon/Services/PopupService.cs index da900dee4..73cbefed7 100644 --- a/Fluent/Services/PopupService.cs +++ b/Fluent.Ribbon/Services/PopupService.cs @@ -1,372 +1,372 @@ -namespace Fluent -{ - using System; - using System.Diagnostics; - using System.Windows; - using System.Windows.Controls; - using System.Windows.Controls.Primitives; - using System.Windows.Input; - using System.Windows.Media; - - /// - /// Dismiss popup mode - /// - public enum DismissPopupMode - { - /// - /// Always dismiss popup - /// - Always, - /// - /// Dismiss only if mouse is not over popup - /// - MouseNotOver - } - - /// - /// Dismiss popup handler - /// - /// - /// - public delegate void DismissPopupEventHandler(object sender, DismissPopupEventArgs e); - - /// - /// Dismiss popup arguments - /// - public class DismissPopupEventArgs : RoutedEventArgs - { - #region Properties - /// - /// Popup dismiss mode - /// - public DismissPopupMode DismissMode { get; set; } - - #endregion - - /// - /// Standard constructor - /// - public DismissPopupEventArgs() - : this(DismissPopupMode.Always) - { - } - - /// - /// Constructor - /// - /// Dismiss mode - public DismissPopupEventArgs(DismissPopupMode dismissMode) - { - this.RoutedEvent = PopupService.DismissPopupEvent; - this.DismissMode = dismissMode; - } - - /// - /// When overridden in a derived class, provides a way to invoke event handlers in a type-specific way, which can increase efficiency over the base implementation. - /// - /// The generic handler / delegate implementation to be invoked.The target on which the provided handler should be invoked. - protected override void InvokeEventHandler(Delegate genericHandler, object genericTarget) - { - var handler = (DismissPopupEventHandler)genericHandler; - handler(genericTarget, this); - } - } - - /// - /// Represent additional popup functionality - /// - public static class PopupService - { - #region DismissPopup - - /// - /// Occurs then popup is dismissed - /// - public static readonly RoutedEvent DismissPopupEvent = EventManager.RegisterRoutedEvent("DismissPopup", RoutingStrategy.Bubble, typeof(DismissPopupEventHandler), typeof(PopupService)); - - /// - /// Raises DismissPopup event (Async) - /// - public static void RaiseDismissPopupEventAsync(object sender, DismissPopupMode mode) - { - var element = sender as UIElement; - - if (element == null) - { - return; - } - - Debug.WriteLine("Dismissing Popup (async)"); - - element.Dispatcher.BeginInvoke((Action)(() => RaiseDismissPopupEvent(sender, mode))); - } - - /// - /// Raises DismissPopup event - /// - public static void RaiseDismissPopupEvent(object sender, DismissPopupMode mode) - { - var element = sender as UIElement; - - if (element == null) - { - return; - } - - Debug.WriteLine("Dismissing Popup"); - - element.RaiseEvent(new DismissPopupEventArgs(mode)); - } - - #endregion - - /// - /// Set needed parameters to control - /// - /// Control type - public static void Attach(Type classType) - { - EventManager.RegisterClassHandler(classType, Mouse.PreviewMouseDownOutsideCapturedElementEvent, new MouseButtonEventHandler(OnClickThroughThunk)); - EventManager.RegisterClassHandler(classType, DismissPopupEvent, new DismissPopupEventHandler(OnDismissPopup)); - EventManager.RegisterClassHandler(classType, FrameworkElement.ContextMenuOpeningEvent, new ContextMenuEventHandler(OnContextMenuOpened), true); - EventManager.RegisterClassHandler(classType, FrameworkElement.ContextMenuClosingEvent, new ContextMenuEventHandler(OnContextMenuClosed), true); - EventManager.RegisterClassHandler(classType, UIElement.LostMouseCaptureEvent, new MouseEventHandler(OnLostMouseCapture)); - } - - /// - /// Handles PreviewMouseDownOutsideCapturedElementEvent event - /// - /// - /// - public static void OnClickThroughThunk(object sender, MouseButtonEventArgs e) - { - ////Debug.WriteLine(string.Format("OnClickThroughThunk: sender = {0}; originalSource = {1}; mouse capture = {2}", sender, e.OriginalSource, Mouse.Captured)); - - if (e.ChangedButton == MouseButton.Left - || e.ChangedButton == MouseButton.Right) - { - if (Mouse.Captured == sender - // Special handling for unknown Popups (for example datepickers used in the ribbon) - || (sender is IDropDownControl - && IsPopupRoot(Mouse.Captured) - ) - ) - { - RaiseDismissPopupEvent(sender, DismissPopupMode.MouseNotOver); - } - } - } - - /// - /// Handles lost mouse capture event - /// - /// - /// - public static void OnLostMouseCapture(object sender, MouseEventArgs e) - { - Debug.WriteLine(string.Format("Sender - {0}", sender)); - Debug.WriteLine(string.Format("OriginalSource - {0}", e.OriginalSource)); - Debug.WriteLine(string.Format("Mouse.Captured - {0}", Mouse.Captured)); - - var control = sender as IDropDownControl; - - if (control == null) - { - return; - } - - if (Mouse.Captured != sender - && control.IsDropDownOpen - && !control.IsContextMenuOpened) - { - var popup = control.DropDownPopup; - - if (popup == null - || popup.Child == null) - { - RaiseDismissPopupEvent(sender, DismissPopupMode.MouseNotOver); - return; - } - - if (e.OriginalSource == sender) - { - // If Ribbon loses capture because something outside popup is clicked - close the popup - if (Mouse.Captured == null - || IsAncestorOf(popup.Child, Mouse.Captured as DependencyObject) == false) - { - RaiseDismissPopupEvent(sender, DismissPopupMode.MouseNotOver); - } - - return; - } - - if (IsAncestorOf(popup.Child, e.OriginalSource as DependencyObject) == false) - { - RaiseDismissPopupEvent(sender, DismissPopupMode.MouseNotOver); - return; - } - - if (e.OriginalSource != null - && Mouse.Captured == null - && (IsPopupRoot(e.OriginalSource) || IsAncestorOf(popup.Child, e.OriginalSource as DependencyObject))) - { - Debug.WriteLine(string.Format("Setting mouse capture to: {0}", sender)); - Mouse.Capture(sender as IInputElement, CaptureMode.SubTree); - e.Handled = true; - return; - } - } - } - - /// - /// Returns true whether parent is ancestor of element - /// - /// Parent - /// Element - /// Returns true whether parent is ancestor of element - public static bool IsAncestorOf(DependencyObject parent, DependencyObject element) - { - while (element != null) - { - if (ReferenceEquals(element, parent)) - { - return true; - } - - element = VisualTreeHelper.GetParent(element) ?? LogicalTreeHelper.GetParent(element); - } - - return false; - } - - /// - /// Handles dismiss popup event - /// - /// - /// - public static void OnDismissPopup(object sender, DismissPopupEventArgs e) - { - var control = sender as IDropDownControl; - - if (control == null) - { - return; - } - - if (e.DismissMode == DismissPopupMode.Always) - { - if (Mouse.Captured == control) - { - Mouse.Capture(null); - } - - control.IsDropDownOpen = false; - } - else - { - if (control.IsDropDownOpen - && !IsMousePhysicallyOver(control.DropDownPopup)) - { - if (Mouse.Captured == control) - { - Mouse.Capture(null); - } - - control.IsDropDownOpen = false; - } - else - { - if (control.IsDropDownOpen - && Mouse.Captured != control) - { - Mouse.Capture(sender as IInputElement, CaptureMode.SubTree); - } - - if (control.IsDropDownOpen) - { - e.Handled = true; - } - } - } - } - - /// - /// Returns true whether mouse is physically over the popup - /// - /// Element - /// Returns true whether mouse is physically over the popup - public static bool IsMousePhysicallyOver(Popup popup) - { - if (popup == null - || popup.Child == null) - { - return false; - } - - return IsMousePhysicallyOver(popup.Child); - } - - /// - /// Returns true whether mouse is physically over the element - /// - /// Element - /// Returns true whether mouse is physically over the element - public static bool IsMousePhysicallyOver(UIElement element) - { - if (element == null) - { - return false; - } - - var position = Mouse.GetPosition(element); - return ((position.X >= 0.0) && (position.Y >= 0.0)) - && ((position.X <= element.RenderSize.Width) && (position.Y <= element.RenderSize.Height)); - } - - /// - /// Handles context menu opened event - /// - /// - /// - public static void OnContextMenuOpened(object sender, ContextMenuEventArgs e) - { - var control = sender as IDropDownControl; - - if (control != null) - { - control.IsContextMenuOpened = true; - // Debug.WriteLine("Context menu opened"); - } - } - - /// - /// Handles context menu closed event - /// - /// - /// - public static void OnContextMenuClosed(object sender, ContextMenuEventArgs e) - { - var control = sender as IDropDownControl; - - if (control != null) - { - //Debug.WriteLine("Context menu closed"); - control.IsContextMenuOpened = false; - RaiseDismissPopupEvent(control, DismissPopupMode.MouseNotOver); - } - } - - private static bool IsPopupRoot(object obj) - { - if (obj == null) - { - return false; - } - - var type = obj.GetType(); - - return type.FullName == "System.Windows.Controls.Primitives.PopupRoot" - || type.Name == "PopupRoot"; - } - } +namespace Fluent +{ + using System; + using System.Diagnostics; + using System.Windows; + using System.Windows.Controls; + using System.Windows.Controls.Primitives; + using System.Windows.Input; + using System.Windows.Media; + + /// + /// Dismiss popup mode + /// + public enum DismissPopupMode + { + /// + /// Always dismiss popup + /// + Always, + /// + /// Dismiss only if mouse is not over popup + /// + MouseNotOver + } + + /// + /// Dismiss popup handler + /// + /// + /// + public delegate void DismissPopupEventHandler(object sender, DismissPopupEventArgs e); + + /// + /// Dismiss popup arguments + /// + public class DismissPopupEventArgs : RoutedEventArgs + { + #region Properties + /// + /// Popup dismiss mode + /// + public DismissPopupMode DismissMode { get; set; } + + #endregion + + /// + /// Standard constructor + /// + public DismissPopupEventArgs() + : this(DismissPopupMode.Always) + { + } + + /// + /// Constructor + /// + /// Dismiss mode + public DismissPopupEventArgs(DismissPopupMode dismissMode) + { + this.RoutedEvent = PopupService.DismissPopupEvent; + this.DismissMode = dismissMode; + } + + /// + /// When overridden in a derived class, provides a way to invoke event handlers in a type-specific way, which can increase efficiency over the base implementation. + /// + /// The generic handler / delegate implementation to be invoked.The target on which the provided handler should be invoked. + protected override void InvokeEventHandler(Delegate genericHandler, object genericTarget) + { + var handler = (DismissPopupEventHandler)genericHandler; + handler(genericTarget, this); + } + } + + /// + /// Represent additional popup functionality + /// + public static class PopupService + { + #region DismissPopup + + /// + /// Occurs then popup is dismissed + /// + public static readonly RoutedEvent DismissPopupEvent = EventManager.RegisterRoutedEvent("DismissPopup", RoutingStrategy.Bubble, typeof(DismissPopupEventHandler), typeof(PopupService)); + + /// + /// Raises DismissPopup event (Async) + /// + public static void RaiseDismissPopupEventAsync(object sender, DismissPopupMode mode) + { + var element = sender as UIElement; + + if (element == null) + { + return; + } + + Debug.WriteLine("Dismissing Popup (async)"); + + element.Dispatcher.BeginInvoke((Action)(() => RaiseDismissPopupEvent(sender, mode))); + } + + /// + /// Raises DismissPopup event + /// + public static void RaiseDismissPopupEvent(object sender, DismissPopupMode mode) + { + var element = sender as UIElement; + + if (element == null) + { + return; + } + + Debug.WriteLine("Dismissing Popup"); + + element.RaiseEvent(new DismissPopupEventArgs(mode)); + } + + #endregion + + /// + /// Set needed parameters to control + /// + /// Control type + public static void Attach(Type classType) + { + EventManager.RegisterClassHandler(classType, Mouse.PreviewMouseDownOutsideCapturedElementEvent, new MouseButtonEventHandler(OnClickThroughThunk)); + EventManager.RegisterClassHandler(classType, DismissPopupEvent, new DismissPopupEventHandler(OnDismissPopup)); + EventManager.RegisterClassHandler(classType, FrameworkElement.ContextMenuOpeningEvent, new ContextMenuEventHandler(OnContextMenuOpened), true); + EventManager.RegisterClassHandler(classType, FrameworkElement.ContextMenuClosingEvent, new ContextMenuEventHandler(OnContextMenuClosed), true); + EventManager.RegisterClassHandler(classType, UIElement.LostMouseCaptureEvent, new MouseEventHandler(OnLostMouseCapture)); + } + + /// + /// Handles PreviewMouseDownOutsideCapturedElementEvent event + /// + /// + /// + public static void OnClickThroughThunk(object sender, MouseButtonEventArgs e) + { + ////Debug.WriteLine(string.Format("OnClickThroughThunk: sender = {0}; originalSource = {1}; mouse capture = {2}", sender, e.OriginalSource, Mouse.Captured)); + + if (e.ChangedButton == MouseButton.Left + || e.ChangedButton == MouseButton.Right) + { + if (Mouse.Captured == sender + // Special handling for unknown Popups (for example datepickers used in the ribbon) + || (sender is IDropDownControl + && IsPopupRoot(Mouse.Captured) + ) + ) + { + RaiseDismissPopupEvent(sender, DismissPopupMode.MouseNotOver); + } + } + } + + /// + /// Handles lost mouse capture event + /// + /// + /// + public static void OnLostMouseCapture(object sender, MouseEventArgs e) + { + Debug.WriteLine(string.Format("Sender - {0}", sender)); + Debug.WriteLine(string.Format("OriginalSource - {0}", e.OriginalSource)); + Debug.WriteLine(string.Format("Mouse.Captured - {0}", Mouse.Captured)); + + var control = sender as IDropDownControl; + + if (control == null) + { + return; + } + + if (Mouse.Captured != sender + && control.IsDropDownOpen + && !control.IsContextMenuOpened) + { + var popup = control.DropDownPopup; + + if (popup == null + || popup.Child == null) + { + RaiseDismissPopupEvent(sender, DismissPopupMode.MouseNotOver); + return; + } + + if (e.OriginalSource == sender) + { + // If Ribbon loses capture because something outside popup is clicked - close the popup + if (Mouse.Captured == null + || IsAncestorOf(popup.Child, Mouse.Captured as DependencyObject) == false) + { + RaiseDismissPopupEvent(sender, DismissPopupMode.MouseNotOver); + } + + return; + } + + if (IsAncestorOf(popup.Child, e.OriginalSource as DependencyObject) == false) + { + RaiseDismissPopupEvent(sender, DismissPopupMode.MouseNotOver); + return; + } + + if (e.OriginalSource != null + && Mouse.Captured == null + && (IsPopupRoot(e.OriginalSource) || IsAncestorOf(popup.Child, e.OriginalSource as DependencyObject))) + { + Debug.WriteLine(string.Format("Setting mouse capture to: {0}", sender)); + Mouse.Capture(sender as IInputElement, CaptureMode.SubTree); + e.Handled = true; + return; + } + } + } + + /// + /// Returns true whether parent is ancestor of element + /// + /// Parent + /// Element + /// Returns true whether parent is ancestor of element + public static bool IsAncestorOf(DependencyObject parent, DependencyObject element) + { + while (element != null) + { + if (ReferenceEquals(element, parent)) + { + return true; + } + + element = VisualTreeHelper.GetParent(element) ?? LogicalTreeHelper.GetParent(element); + } + + return false; + } + + /// + /// Handles dismiss popup event + /// + /// + /// + public static void OnDismissPopup(object sender, DismissPopupEventArgs e) + { + var control = sender as IDropDownControl; + + if (control == null) + { + return; + } + + if (e.DismissMode == DismissPopupMode.Always) + { + if (Mouse.Captured == control) + { + Mouse.Capture(null); + } + + control.IsDropDownOpen = false; + } + else + { + if (control.IsDropDownOpen + && !IsMousePhysicallyOver(control.DropDownPopup)) + { + if (Mouse.Captured == control) + { + Mouse.Capture(null); + } + + control.IsDropDownOpen = false; + } + else + { + if (control.IsDropDownOpen + && Mouse.Captured != control) + { + Mouse.Capture(sender as IInputElement, CaptureMode.SubTree); + } + + if (control.IsDropDownOpen) + { + e.Handled = true; + } + } + } + } + + /// + /// Returns true whether mouse is physically over the popup + /// + /// Element + /// Returns true whether mouse is physically over the popup + public static bool IsMousePhysicallyOver(Popup popup) + { + if (popup == null + || popup.Child == null) + { + return false; + } + + return IsMousePhysicallyOver(popup.Child); + } + + /// + /// Returns true whether mouse is physically over the element + /// + /// Element + /// Returns true whether mouse is physically over the element + public static bool IsMousePhysicallyOver(UIElement element) + { + if (element == null) + { + return false; + } + + var position = Mouse.GetPosition(element); + return ((position.X >= 0.0) && (position.Y >= 0.0)) + && ((position.X <= element.RenderSize.Width) && (position.Y <= element.RenderSize.Height)); + } + + /// + /// Handles context menu opened event + /// + /// + /// + public static void OnContextMenuOpened(object sender, ContextMenuEventArgs e) + { + var control = sender as IDropDownControl; + + if (control != null) + { + control.IsContextMenuOpened = true; + // Debug.WriteLine("Context menu opened"); + } + } + + /// + /// Handles context menu closed event + /// + /// + /// + public static void OnContextMenuClosed(object sender, ContextMenuEventArgs e) + { + var control = sender as IDropDownControl; + + if (control != null) + { + //Debug.WriteLine("Context menu closed"); + control.IsContextMenuOpened = false; + RaiseDismissPopupEvent(control, DismissPopupMode.MouseNotOver); + } + } + + private static bool IsPopupRoot(object obj) + { + if (obj == null) + { + return false; + } + + var type = obj.GetType(); + + return type.FullName == "System.Windows.Controls.Primitives.PopupRoot" + || type.Name == "PopupRoot"; + } + } } \ No newline at end of file diff --git a/Fluent/Services/ToolTipService.cs b/Fluent.Ribbon/Services/ToolTipService.cs similarity index 97% rename from Fluent/Services/ToolTipService.cs rename to Fluent.Ribbon/Services/ToolTipService.cs index 4a080d350..3e37f2819 100644 --- a/Fluent/Services/ToolTipService.cs +++ b/Fluent.Ribbon/Services/ToolTipService.cs @@ -1,26 +1,26 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Windows; - -namespace Fluent -{ - /// - /// Represents additional toltip functionality - /// - public static class ToolTipService - { - /// - /// Attach ooltip properties to control - /// - /// Control type - public static void Attach(Type type) - { - System.Windows.Controls.ToolTipService.ShowOnDisabledProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(true)); - System.Windows.Controls.ToolTipService.InitialShowDelayProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(900)); - System.Windows.Controls.ToolTipService.BetweenShowDelayProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(0)); - System.Windows.Controls.ToolTipService.ShowDurationProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(20000)); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; + +namespace Fluent +{ + /// + /// Represents additional toltip functionality + /// + public static class ToolTipService + { + /// + /// Attach ooltip properties to control + /// + /// Control type + public static void Attach(Type type) + { + System.Windows.Controls.ToolTipService.ShowOnDisabledProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(true)); + System.Windows.Controls.ToolTipService.InitialShowDelayProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(900)); + System.Windows.Controls.ToolTipService.BetweenShowDelayProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(0)); + System.Windows.Controls.ToolTipService.ShowDurationProperty.OverrideMetadata(type, new FrameworkPropertyMetadata(20000)); + } + } +} diff --git a/Fluent/Themes/Generic.xaml b/Fluent.Ribbon/Themes/Generic.xaml similarity index 100% rename from Fluent/Themes/Generic.xaml rename to Fluent.Ribbon/Themes/Generic.xaml diff --git a/Fluent/Themes/Generic/Common.xaml b/Fluent.Ribbon/Themes/Generic/Common.xaml similarity index 100% rename from Fluent/Themes/Generic/Common.xaml rename to Fluent.Ribbon/Themes/Generic/Common.xaml diff --git a/Fluent/Themes/Generic/Controls/EmptyFocusStyle.xaml b/Fluent.Ribbon/Themes/Generic/Controls/EmptyFocusStyle.xaml similarity index 100% rename from Fluent/Themes/Generic/Controls/EmptyFocusStyle.xaml rename to Fluent.Ribbon/Themes/Generic/Controls/EmptyFocusStyle.xaml diff --git a/Fluent/Themes/Generic/Controls/GalleryGroupContainer.xaml b/Fluent.Ribbon/Themes/Generic/Controls/GalleryGroupContainer.xaml similarity index 100% rename from Fluent/Themes/Generic/Controls/GalleryGroupContainer.xaml rename to Fluent.Ribbon/Themes/Generic/Controls/GalleryGroupContainer.xaml diff --git a/Fluent/Themes/Generic/Controls/MenuSeparator.xaml b/Fluent.Ribbon/Themes/Generic/Controls/MenuSeparator.xaml similarity index 100% rename from Fluent/Themes/Generic/Controls/MenuSeparator.xaml rename to Fluent.Ribbon/Themes/Generic/Controls/MenuSeparator.xaml diff --git a/Fluent/Themes/Generic/Controls/RadioButton.xaml b/Fluent.Ribbon/Themes/Generic/Controls/RadioButton.xaml similarity index 100% rename from Fluent/Themes/Generic/Controls/RadioButton.xaml rename to Fluent.Ribbon/Themes/Generic/Controls/RadioButton.xaml diff --git a/Fluent/Themes/Generic/Controls/RibbonMenu.xaml b/Fluent.Ribbon/Themes/Generic/Controls/RibbonMenu.xaml similarity index 100% rename from Fluent/Themes/Generic/Controls/RibbonMenu.xaml rename to Fluent.Ribbon/Themes/Generic/Controls/RibbonMenu.xaml diff --git a/Fluent/Themes/Generic/Controls/RibbonScrollViewer.xaml b/Fluent.Ribbon/Themes/Generic/Controls/RibbonScrollViewer.xaml similarity index 100% rename from Fluent/Themes/Generic/Controls/RibbonScrollViewer.xaml rename to Fluent.Ribbon/Themes/Generic/Controls/RibbonScrollViewer.xaml diff --git a/Fluent/Themes/Generic/Controls/RibbonTextBox.xaml b/Fluent.Ribbon/Themes/Generic/Controls/RibbonTextBox.xaml similarity index 100% rename from Fluent/Themes/Generic/Controls/RibbonTextBox.xaml rename to Fluent.Ribbon/Themes/Generic/Controls/RibbonTextBox.xaml diff --git a/Fluent/Themes/Generic/Controls/RibbonTitleBar.xaml b/Fluent.Ribbon/Themes/Generic/Controls/RibbonTitleBar.xaml similarity index 100% rename from Fluent/Themes/Generic/Controls/RibbonTitleBar.xaml rename to Fluent.Ribbon/Themes/Generic/Controls/RibbonTitleBar.xaml diff --git a/Fluent/Themes/Generic/Controls/RibbonToolBar.xaml b/Fluent.Ribbon/Themes/Generic/Controls/RibbonToolBar.xaml similarity index 100% rename from Fluent/Themes/Generic/Controls/RibbonToolBar.xaml rename to Fluent.Ribbon/Themes/Generic/Controls/RibbonToolBar.xaml diff --git a/Fluent/Themes/Generic/Controls/RibbonToolBarControlGroup.xaml b/Fluent.Ribbon/Themes/Generic/Controls/RibbonToolBarControlGroup.xaml similarity index 100% rename from Fluent/Themes/Generic/Controls/RibbonToolBarControlGroup.xaml rename to Fluent.Ribbon/Themes/Generic/Controls/RibbonToolBarControlGroup.xaml diff --git a/Fluent/Themes/Generic/Controls/SeparatorTabItem.xaml b/Fluent.Ribbon/Themes/Generic/Controls/SeparatorTabItem.xaml similarity index 100% rename from Fluent/Themes/Generic/Controls/SeparatorTabItem.xaml rename to Fluent.Ribbon/Themes/Generic/Controls/SeparatorTabItem.xaml diff --git a/Fluent/Themes/Generic/Controls/TextBox.xaml b/Fluent.Ribbon/Themes/Generic/Controls/TextBox.xaml similarity index 100% rename from Fluent/Themes/Generic/Controls/TextBox.xaml rename to Fluent.Ribbon/Themes/Generic/Controls/TextBox.xaml diff --git a/Fluent/Themes/Generic/Controls/TwoLineLabel.xaml b/Fluent.Ribbon/Themes/Generic/Controls/TwoLineLabel.xaml similarity index 100% rename from Fluent/Themes/Generic/Controls/TwoLineLabel.xaml rename to Fluent.Ribbon/Themes/Generic/Controls/TwoLineLabel.xaml diff --git a/Fluent/Themes/Office2010/Black.xaml b/Fluent.Ribbon/Themes/Office2010/Black.xaml similarity index 98% rename from Fluent/Themes/Office2010/Black.xaml rename to Fluent.Ribbon/Themes/Office2010/Black.xaml index 17324face..11fba7796 100644 --- a/Fluent/Themes/Office2010/Black.xaml +++ b/Fluent.Ribbon/Themes/Office2010/Black.xaml @@ -1,6 +1,6 @@ - - - - + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2010/Blue.xaml b/Fluent.Ribbon/Themes/Office2010/Blue.xaml similarity index 98% rename from Fluent/Themes/Office2010/Blue.xaml rename to Fluent.Ribbon/Themes/Office2010/Blue.xaml index 058c167b8..b03d57609 100644 --- a/Fluent/Themes/Office2010/Blue.xaml +++ b/Fluent.Ribbon/Themes/Office2010/Blue.xaml @@ -1,6 +1,6 @@ - - - - + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2010/Colors/Colors.xaml b/Fluent.Ribbon/Themes/Office2010/Colors/Colors.xaml similarity index 97% rename from Fluent/Themes/Office2010/Colors/Colors.xaml rename to Fluent.Ribbon/Themes/Office2010/Colors/Colors.xaml index e1f554b2e..d4df2e0ce 100644 --- a/Fluent/Themes/Office2010/Colors/Colors.xaml +++ b/Fluent.Ribbon/Themes/Office2010/Colors/Colors.xaml @@ -1,399 +1,399 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2010/Colors/ColorsBlack.xaml b/Fluent.Ribbon/Themes/Office2010/Colors/ColorsBlack.xaml similarity index 97% rename from Fluent/Themes/Office2010/Colors/ColorsBlack.xaml rename to Fluent.Ribbon/Themes/Office2010/Colors/ColorsBlack.xaml index 17e4ddca7..8ad5cccac 100644 --- a/Fluent/Themes/Office2010/Colors/ColorsBlack.xaml +++ b/Fluent.Ribbon/Themes/Office2010/Colors/ColorsBlack.xaml @@ -1,487 +1,487 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #D8484848 - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #D8484848 + \ No newline at end of file diff --git a/Fluent/Themes/Office2010/Colors/ColorsBlue.xaml b/Fluent.Ribbon/Themes/Office2010/Colors/ColorsBlue.xaml similarity index 97% rename from Fluent/Themes/Office2010/Colors/ColorsBlue.xaml rename to Fluent.Ribbon/Themes/Office2010/Colors/ColorsBlue.xaml index 86f874f4c..6c2e25243 100644 --- a/Fluent/Themes/Office2010/Colors/ColorsBlue.xaml +++ b/Fluent.Ribbon/Themes/Office2010/Colors/ColorsBlue.xaml @@ -1,484 +1,484 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #D8F9F7FA + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #D8F9F7FA \ No newline at end of file diff --git a/Fluent/Themes/Office2010/Colors/ColorsSilver.xaml b/Fluent.Ribbon/Themes/Office2010/Colors/ColorsSilver.xaml similarity index 97% rename from Fluent/Themes/Office2010/Colors/ColorsSilver.xaml rename to Fluent.Ribbon/Themes/Office2010/Colors/ColorsSilver.xaml index fc1096691..8bfbb1ec7 100644 --- a/Fluent/Themes/Office2010/Colors/ColorsSilver.xaml +++ b/Fluent.Ribbon/Themes/Office2010/Colors/ColorsSilver.xaml @@ -1,475 +1,475 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #D8FFFFFF - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #D8FFFFFF + \ No newline at end of file diff --git a/Fluent/Themes/Office2010/Controls/ApplicationMenu.xaml b/Fluent.Ribbon/Themes/Office2010/Controls/ApplicationMenu.xaml similarity index 98% rename from Fluent/Themes/Office2010/Controls/ApplicationMenu.xaml rename to Fluent.Ribbon/Themes/Office2010/Controls/ApplicationMenu.xaml index c8a86b2db..0b5205add 100644 --- a/Fluent/Themes/Office2010/Controls/ApplicationMenu.xaml +++ b/Fluent.Ribbon/Themes/Office2010/Controls/ApplicationMenu.xaml @@ -1,402 +1,402 @@ - - - - - - - - - - + + + + + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2010/Controls/ApplicationMenuItem.xaml b/Fluent.Ribbon/Themes/Office2010/Controls/ApplicationMenuItem.xaml similarity index 97% rename from Fluent/Themes/Office2010/Controls/ApplicationMenuItem.xaml rename to Fluent.Ribbon/Themes/Office2010/Controls/ApplicationMenuItem.xaml index 2303a7abb..58ef32165 100644 --- a/Fluent/Themes/Office2010/Controls/ApplicationMenuItem.xaml +++ b/Fluent.Ribbon/Themes/Office2010/Controls/ApplicationMenuItem.xaml @@ -1,1715 +1,1715 @@ - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2010/Controls/Backstage.xaml b/Fluent.Ribbon/Themes/Office2010/Controls/Backstage.xaml similarity index 98% rename from Fluent/Themes/Office2010/Controls/Backstage.xaml rename to Fluent.Ribbon/Themes/Office2010/Controls/Backstage.xaml index 7d2c195e7..ba68337de 100644 --- a/Fluent/Themes/Office2010/Controls/Backstage.xaml +++ b/Fluent.Ribbon/Themes/Office2010/Controls/Backstage.xaml @@ -1,295 +1,295 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2010/Controls/BackstageControls.xaml b/Fluent.Ribbon/Themes/Office2010/Controls/BackstageControls.xaml similarity index 98% rename from Fluent/Themes/Office2010/Controls/BackstageControls.xaml rename to Fluent.Ribbon/Themes/Office2010/Controls/BackstageControls.xaml index 045f23476..e5c6b988d 100644 --- a/Fluent/Themes/Office2010/Controls/BackstageControls.xaml +++ b/Fluent.Ribbon/Themes/Office2010/Controls/BackstageControls.xaml @@ -1,1578 +1,1578 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2010/Controls/BackstageTabControl.xaml b/Fluent.Ribbon/Themes/Office2010/Controls/BackstageTabControl.xaml similarity index 97% rename from Fluent/Themes/Office2010/Controls/BackstageTabControl.xaml rename to Fluent.Ribbon/Themes/Office2010/Controls/BackstageTabControl.xaml index b53c34507..78445ef55 100644 --- a/Fluent/Themes/Office2010/Controls/BackstageTabControl.xaml +++ b/Fluent.Ribbon/Themes/Office2010/Controls/BackstageTabControl.xaml @@ -1,284 +1,284 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2010/Controls/BackstageTabItem.xaml b/Fluent.Ribbon/Themes/Office2010/Controls/BackstageTabItem.xaml similarity index 98% rename from Fluent/Themes/Office2010/Controls/BackstageTabItem.xaml rename to Fluent.Ribbon/Themes/Office2010/Controls/BackstageTabItem.xaml index f77c837fc..9b449520c 100644 --- a/Fluent/Themes/Office2010/Controls/BackstageTabItem.xaml +++ b/Fluent.Ribbon/Themes/Office2010/Controls/BackstageTabItem.xaml @@ -1,128 +1,128 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2010/Controls/Button.xaml b/Fluent.Ribbon/Themes/Office2010/Controls/Button.xaml similarity index 97% rename from Fluent/Themes/Office2010/Controls/Button.xaml rename to Fluent.Ribbon/Themes/Office2010/Controls/Button.xaml index d7391b65c..b921cb8d8 100644 --- a/Fluent/Themes/Office2010/Controls/Button.xaml +++ b/Fluent.Ribbon/Themes/Office2010/Controls/Button.xaml @@ -1,226 +1,226 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2010/Controls/CheckBox.xaml b/Fluent.Ribbon/Themes/Office2010/Controls/CheckBox.xaml similarity index 98% rename from Fluent/Themes/Office2010/Controls/CheckBox.xaml rename to Fluent.Ribbon/Themes/Office2010/Controls/CheckBox.xaml index c86f83f4e..9fe531046 100644 --- a/Fluent/Themes/Office2010/Controls/CheckBox.xaml +++ b/Fluent.Ribbon/Themes/Office2010/Controls/CheckBox.xaml @@ -1,190 +1,190 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2010/Controls/ComboBox.xaml b/Fluent.Ribbon/Themes/Office2010/Controls/ComboBox.xaml similarity index 98% rename from Fluent/Themes/Office2010/Controls/ComboBox.xaml rename to Fluent.Ribbon/Themes/Office2010/Controls/ComboBox.xaml index c39d92c58..0aa3b0013 100644 --- a/Fluent/Themes/Office2010/Controls/ComboBox.xaml +++ b/Fluent.Ribbon/Themes/Office2010/Controls/ComboBox.xaml @@ -1,673 +1,673 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2010/Controls/ComboBoxItem.xaml b/Fluent.Ribbon/Themes/Office2010/Controls/ComboBoxItem.xaml similarity index 98% rename from Fluent/Themes/Office2010/Controls/ComboBoxItem.xaml rename to Fluent.Ribbon/Themes/Office2010/Controls/ComboBoxItem.xaml index 85979a5ba..41dcd8fc1 100644 --- a/Fluent/Themes/Office2010/Controls/ComboBoxItem.xaml +++ b/Fluent.Ribbon/Themes/Office2010/Controls/ComboBoxItem.xaml @@ -1,124 +1,124 @@ - - - - + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2010/Controls/DropDownButton.xaml b/Fluent.Ribbon/Themes/Office2010/Controls/DropDownButton.xaml similarity index 98% rename from Fluent/Themes/Office2010/Controls/DropDownButton.xaml rename to Fluent.Ribbon/Themes/Office2010/Controls/DropDownButton.xaml index d50580131..e211417cc 100644 --- a/Fluent/Themes/Office2010/Controls/DropDownButton.xaml +++ b/Fluent.Ribbon/Themes/Office2010/Controls/DropDownButton.xaml @@ -1,357 +1,357 @@ - - - - - - - - - + + + - - - - - + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2010/Controls/GalleryItem.xaml b/Fluent.Ribbon/Themes/Office2010/Controls/GalleryItem.xaml similarity index 98% rename from Fluent/Themes/Office2010/Controls/GalleryItem.xaml rename to Fluent.Ribbon/Themes/Office2010/Controls/GalleryItem.xaml index e6c83b272..8e2ac10f4 100644 --- a/Fluent/Themes/Office2010/Controls/GalleryItem.xaml +++ b/Fluent.Ribbon/Themes/Office2010/Controls/GalleryItem.xaml @@ -1,88 +1,88 @@ - - + + \ No newline at end of file diff --git a/Fluent/Themes/Office2010/Controls/InRibbonGallery.xaml b/Fluent.Ribbon/Themes/Office2010/Controls/InRibbonGallery.xaml similarity index 98% rename from Fluent/Themes/Office2010/Controls/InRibbonGallery.xaml rename to Fluent.Ribbon/Themes/Office2010/Controls/InRibbonGallery.xaml index 3b9ae2abc..ce57d96c8 100644 --- a/Fluent/Themes/Office2010/Controls/InRibbonGallery.xaml +++ b/Fluent.Ribbon/Themes/Office2010/Controls/InRibbonGallery.xaml @@ -1,712 +1,712 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2010/Controls/KeyTip.xaml b/Fluent.Ribbon/Themes/Office2010/Controls/KeyTip.xaml similarity index 97% rename from Fluent/Themes/Office2010/Controls/KeyTip.xaml rename to Fluent.Ribbon/Themes/Office2010/Controls/KeyTip.xaml index 43c32f4c3..e9e7626c3 100644 --- a/Fluent/Themes/Office2010/Controls/KeyTip.xaml +++ b/Fluent.Ribbon/Themes/Office2010/Controls/KeyTip.xaml @@ -1,39 +1,39 @@ - - - + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2010/Controls/Menu.xaml b/Fluent.Ribbon/Themes/Office2010/Controls/Menu.xaml similarity index 98% rename from Fluent/Themes/Office2010/Controls/Menu.xaml rename to Fluent.Ribbon/Themes/Office2010/Controls/Menu.xaml index 50842639c..4f255ec4e 100644 --- a/Fluent/Themes/Office2010/Controls/Menu.xaml +++ b/Fluent.Ribbon/Themes/Office2010/Controls/Menu.xaml @@ -1,236 +1,236 @@ - - - - - - - - - + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2010/Controls/Ribbon.xaml b/Fluent.Ribbon/Themes/Office2010/Controls/Ribbon.xaml similarity index 98% rename from Fluent/Themes/Office2010/Controls/Ribbon.xaml rename to Fluent.Ribbon/Themes/Office2010/Controls/Ribbon.xaml index 3a3f5739c..4b571052d 100644 --- a/Fluent/Themes/Office2010/Controls/Ribbon.xaml +++ b/Fluent.Ribbon/Themes/Office2010/Controls/Ribbon.xaml @@ -1,141 +1,141 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2010/Controls/RibbonContextualTabGroup.xaml b/Fluent.Ribbon/Themes/Office2010/Controls/RibbonContextualTabGroup.xaml similarity index 98% rename from Fluent/Themes/Office2010/Controls/RibbonContextualTabGroup.xaml rename to Fluent.Ribbon/Themes/Office2010/Controls/RibbonContextualTabGroup.xaml index b28c1a673..37ebfcc17 100644 --- a/Fluent/Themes/Office2010/Controls/RibbonContextualTabGroup.xaml +++ b/Fluent.Ribbon/Themes/Office2010/Controls/RibbonContextualTabGroup.xaml @@ -1,123 +1,123 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2010/Controls/RibbonGroupBox.xaml b/Fluent.Ribbon/Themes/Office2010/Controls/RibbonGroupBox.xaml similarity index 98% rename from Fluent/Themes/Office2010/Controls/RibbonGroupBox.xaml rename to Fluent.Ribbon/Themes/Office2010/Controls/RibbonGroupBox.xaml index a3490b3a3..46c63bce1 100644 --- a/Fluent/Themes/Office2010/Controls/RibbonGroupBox.xaml +++ b/Fluent.Ribbon/Themes/Office2010/Controls/RibbonGroupBox.xaml @@ -1,815 +1,815 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2010/Controls/RibbonStatusBar.xaml b/Fluent.Ribbon/Themes/Office2010/Controls/RibbonStatusBar.xaml similarity index 97% rename from Fluent/Themes/Office2010/Controls/RibbonStatusBar.xaml rename to Fluent.Ribbon/Themes/Office2010/Controls/RibbonStatusBar.xaml index dc17dce0f..72d51ae0e 100644 --- a/Fluent/Themes/Office2010/Controls/RibbonStatusBar.xaml +++ b/Fluent.Ribbon/Themes/Office2010/Controls/RibbonStatusBar.xaml @@ -1,221 +1,221 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2010/Controls/RibbonTabItem.xaml b/Fluent.Ribbon/Themes/Office2010/Controls/RibbonTabItem.xaml similarity index 98% rename from Fluent/Themes/Office2010/Controls/RibbonTabItem.xaml rename to Fluent.Ribbon/Themes/Office2010/Controls/RibbonTabItem.xaml index 69bf5e7f6..9bbf06d81 100644 --- a/Fluent/Themes/Office2010/Controls/RibbonTabItem.xaml +++ b/Fluent.Ribbon/Themes/Office2010/Controls/RibbonTabItem.xaml @@ -1,518 +1,518 @@ - - + + \ No newline at end of file diff --git a/Fluent/Themes/Office2010/Controls/ScreenTip.xaml b/Fluent.Ribbon/Themes/Office2010/Controls/ScreenTip.xaml similarity index 98% rename from Fluent/Themes/Office2010/Controls/ScreenTip.xaml rename to Fluent.Ribbon/Themes/Office2010/Controls/ScreenTip.xaml index 35d15e42a..aebe75cc7 100644 --- a/Fluent/Themes/Office2010/Controls/ScreenTip.xaml +++ b/Fluent.Ribbon/Themes/Office2010/Controls/ScreenTip.xaml @@ -1,447 +1,447 @@ - - - - + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2010/Controls/ScrollBar.xaml b/Fluent.Ribbon/Themes/Office2010/Controls/ScrollBar.xaml similarity index 98% rename from Fluent/Themes/Office2010/Controls/ScrollBar.xaml rename to Fluent.Ribbon/Themes/Office2010/Controls/ScrollBar.xaml index 1283b26bc..47bc8001c 100644 --- a/Fluent/Themes/Office2010/Controls/ScrollBar.xaml +++ b/Fluent.Ribbon/Themes/Office2010/Controls/ScrollBar.xaml @@ -1,637 +1,637 @@ - - - - - - - - - - - - - - - - - - - - - - - - - 36 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 36 - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + 36 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 36 + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2010/Controls/ScrollBarWhite.xaml b/Fluent.Ribbon/Themes/Office2010/Controls/ScrollBarWhite.xaml similarity index 98% rename from Fluent/Themes/Office2010/Controls/ScrollBarWhite.xaml rename to Fluent.Ribbon/Themes/Office2010/Controls/ScrollBarWhite.xaml index 2fa80d463..420d720d2 100644 --- a/Fluent/Themes/Office2010/Controls/ScrollBarWhite.xaml +++ b/Fluent.Ribbon/Themes/Office2010/Controls/ScrollBarWhite.xaml @@ -1,576 +1,576 @@ - - - - - - - - - - - - - - - - - - - - - - - - - 36 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 36 - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + 36 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 36 + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2010/Controls/Slider.xaml b/Fluent.Ribbon/Themes/Office2010/Controls/Slider.xaml similarity index 98% rename from Fluent/Themes/Office2010/Controls/Slider.xaml rename to Fluent.Ribbon/Themes/Office2010/Controls/Slider.xaml index bf802f094..b3fee1304 100644 --- a/Fluent/Themes/Office2010/Controls/Slider.xaml +++ b/Fluent.Ribbon/Themes/Office2010/Controls/Slider.xaml @@ -1,344 +1,344 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2010/Controls/Spinner.xaml b/Fluent.Ribbon/Themes/Office2010/Controls/Spinner.xaml similarity index 98% rename from Fluent/Themes/Office2010/Controls/Spinner.xaml rename to Fluent.Ribbon/Themes/Office2010/Controls/Spinner.xaml index 3b67c1f6c..136887554 100644 --- a/Fluent/Themes/Office2010/Controls/Spinner.xaml +++ b/Fluent.Ribbon/Themes/Office2010/Controls/Spinner.xaml @@ -1,311 +1,311 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2010/Controls/SplitButton.xaml b/Fluent.Ribbon/Themes/Office2010/Controls/SplitButton.xaml similarity index 98% rename from Fluent/Themes/Office2010/Controls/SplitButton.xaml rename to Fluent.Ribbon/Themes/Office2010/Controls/SplitButton.xaml index 688e053b4..d61e3c3e0 100644 --- a/Fluent/Themes/Office2010/Controls/SplitButton.xaml +++ b/Fluent.Ribbon/Themes/Office2010/Controls/SplitButton.xaml @@ -1,739 +1,739 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2010/Controls/StatusBar.xaml b/Fluent.Ribbon/Themes/Office2010/Controls/StatusBar.xaml similarity index 97% rename from Fluent/Themes/Office2010/Controls/StatusBar.xaml rename to Fluent.Ribbon/Themes/Office2010/Controls/StatusBar.xaml index 9703e20b0..d1540e1ab 100644 --- a/Fluent/Themes/Office2010/Controls/StatusBar.xaml +++ b/Fluent.Ribbon/Themes/Office2010/Controls/StatusBar.xaml @@ -1,66 +1,66 @@ - - - - - - + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2010/Controls/ToggleButton.xaml b/Fluent.Ribbon/Themes/Office2010/Controls/ToggleButton.xaml similarity index 98% rename from Fluent/Themes/Office2010/Controls/ToggleButton.xaml rename to Fluent.Ribbon/Themes/Office2010/Controls/ToggleButton.xaml index 76644bc6c..e33e57548 100644 --- a/Fluent/Themes/Office2010/Controls/ToggleButton.xaml +++ b/Fluent.Ribbon/Themes/Office2010/Controls/ToggleButton.xaml @@ -1,246 +1,246 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2010/Effects/Grayscale.fx b/Fluent.Ribbon/Themes/Office2010/Effects/Grayscale.fx similarity index 97% rename from Fluent/Themes/Office2010/Effects/Grayscale.fx rename to Fluent.Ribbon/Themes/Office2010/Effects/Grayscale.fx index ba5be3acb..35f75308a 100644 --- a/Fluent/Themes/Office2010/Effects/Grayscale.fx +++ b/Fluent.Ribbon/Themes/Office2010/Effects/Grayscale.fx @@ -1,31 +1,31 @@ -/// MonochromeEffect -/// Shazzam.Shaders -/// An effect that turns the input into shades of a single color. - -//----------------------------------------------------------------------------------------- -// Shader constant register mappings (scalars - float, double, Point, Color, Point3D, etc.) -//----------------------------------------------------------------------------------------- - -/// The color used to tint the input. -/// Yellow -float4 FilterColor : register(C0); - -//-------------------------------------------------------------------------------------- -// Sampler Inputs (Brushes, including ImplicitInput) -//-------------------------------------------------------------------------------------- - -sampler2D implicitInputSampler : register(S0); - -//-------------------------------------------------------------------------------------- -// Pixel Shader -//-------------------------------------------------------------------------------------- - -float4 main(float2 uv : TEXCOORD) : COLOR -{ - float4 srcColor = tex2D(implicitInputSampler, uv); - float3 rgb = srcColor.rgb; - float3 luminance = dot(rgb, float3(0.30, 0.59, 0.11)); - return float4(luminance * FilterColor.rgb, srcColor.a); -} - - +/// MonochromeEffect +/// Shazzam.Shaders +/// An effect that turns the input into shades of a single color. + +//----------------------------------------------------------------------------------------- +// Shader constant register mappings (scalars - float, double, Point, Color, Point3D, etc.) +//----------------------------------------------------------------------------------------- + +/// The color used to tint the input. +/// Yellow +float4 FilterColor : register(C0); + +//-------------------------------------------------------------------------------------- +// Sampler Inputs (Brushes, including ImplicitInput) +//-------------------------------------------------------------------------------------- + +sampler2D implicitInputSampler : register(S0); + +//-------------------------------------------------------------------------------------- +// Pixel Shader +//-------------------------------------------------------------------------------------- + +float4 main(float2 uv : TEXCOORD) : COLOR +{ + float4 srcColor = tex2D(implicitInputSampler, uv); + float3 rgb = srcColor.rgb; + float3 luminance = dot(rgb, float3(0.30, 0.59, 0.11)); + return float4(luminance * FilterColor.rgb, srcColor.a); +} + + diff --git a/Fluent/Themes/Office2010/Effects/Grayscale.ps b/Fluent.Ribbon/Themes/Office2010/Effects/Grayscale.ps similarity index 100% rename from Fluent/Themes/Office2010/Effects/Grayscale.ps rename to Fluent.Ribbon/Themes/Office2010/Effects/Grayscale.ps diff --git a/Fluent/Themes/Office2010/Generic.txt b/Fluent.Ribbon/Themes/Office2010/Generic.txt similarity index 100% rename from Fluent/Themes/Office2010/Generic.txt rename to Fluent.Ribbon/Themes/Office2010/Generic.txt diff --git a/Fluent.Ribbon/Themes/Office2010/Generic.xaml b/Fluent.Ribbon/Themes/Office2010/Generic.xaml new file mode 100644 index 000000000..bb6ac7bec --- /dev/null +++ b/Fluent.Ribbon/Themes/Office2010/Generic.xaml @@ -0,0 +1,7705 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #D8FFFFFF + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 36 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 36 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 36 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 36 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2010/Silver.xaml b/Fluent.Ribbon/Themes/Office2010/Silver.xaml similarity index 98% rename from Fluent/Themes/Office2010/Silver.xaml rename to Fluent.Ribbon/Themes/Office2010/Silver.xaml index 22e3bafa1..3752c76b2 100644 --- a/Fluent/Themes/Office2010/Silver.xaml +++ b/Fluent.Ribbon/Themes/Office2010/Silver.xaml @@ -1,6 +1,6 @@ - - - - + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2013/Colors/Colors.xaml b/Fluent.Ribbon/Themes/Office2013/Colors/Colors.xaml similarity index 97% rename from Fluent/Themes/Office2013/Colors/Colors.xaml rename to Fluent.Ribbon/Themes/Office2013/Colors/Colors.xaml index c305ad338..8c38659e8 100644 --- a/Fluent/Themes/Office2013/Colors/Colors.xaml +++ b/Fluent.Ribbon/Themes/Office2013/Colors/Colors.xaml @@ -1,426 +1,426 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #FF000000 - #FFFFFFFF - #FFCCCCCC - #FFD8D8D9 - - #00FFFFFF - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #FF000000 + #FFFFFFFF + #FFCCCCCC + #FFD8D8D9 + + #00FFFFFF + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2013/Colors/ColorsWhite.xaml b/Fluent.Ribbon/Themes/Office2013/Colors/ColorsWhite.xaml similarity index 97% rename from Fluent/Themes/Office2013/Colors/ColorsWhite.xaml rename to Fluent.Ribbon/Themes/Office2013/Colors/ColorsWhite.xaml index 2f1185245..5eb4dd004 100644 --- a/Fluent/Themes/Office2013/Colors/ColorsWhite.xaml +++ b/Fluent.Ribbon/Themes/Office2013/Colors/ColorsWhite.xaml @@ -1,300 +1,300 @@ - - - #2B579A - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + #2B579A + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2013/Controls/ApplicationMenu.xaml b/Fluent.Ribbon/Themes/Office2013/Controls/ApplicationMenu.xaml similarity index 98% rename from Fluent/Themes/Office2013/Controls/ApplicationMenu.xaml rename to Fluent.Ribbon/Themes/Office2013/Controls/ApplicationMenu.xaml index 441aaa1a4..7d5da12ce 100644 --- a/Fluent/Themes/Office2013/Controls/ApplicationMenu.xaml +++ b/Fluent.Ribbon/Themes/Office2013/Controls/ApplicationMenu.xaml @@ -1,173 +1,173 @@ - - - - + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2013/Controls/Backstage.xaml b/Fluent.Ribbon/Themes/Office2013/Controls/Backstage.xaml similarity index 97% rename from Fluent/Themes/Office2013/Controls/Backstage.xaml rename to Fluent.Ribbon/Themes/Office2013/Controls/Backstage.xaml index 2433e34c0..7b9e03612 100644 --- a/Fluent/Themes/Office2013/Controls/Backstage.xaml +++ b/Fluent.Ribbon/Themes/Office2013/Controls/Backstage.xaml @@ -1,87 +1,87 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2013/Controls/BackstageControls.xaml b/Fluent.Ribbon/Themes/Office2013/Controls/BackstageControls.xaml similarity index 98% rename from Fluent/Themes/Office2013/Controls/BackstageControls.xaml rename to Fluent.Ribbon/Themes/Office2013/Controls/BackstageControls.xaml index bff2cddfc..bff858e29 100644 --- a/Fluent/Themes/Office2013/Controls/BackstageControls.xaml +++ b/Fluent.Ribbon/Themes/Office2013/Controls/BackstageControls.xaml @@ -1,1288 +1,1288 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2013/Controls/BackstageTabControl.xaml b/Fluent.Ribbon/Themes/Office2013/Controls/BackstageTabControl.xaml similarity index 97% rename from Fluent/Themes/Office2013/Controls/BackstageTabControl.xaml rename to Fluent.Ribbon/Themes/Office2013/Controls/BackstageTabControl.xaml index 37772f1af..9f113256f 100644 --- a/Fluent/Themes/Office2013/Controls/BackstageTabControl.xaml +++ b/Fluent.Ribbon/Themes/Office2013/Controls/BackstageTabControl.xaml @@ -1,286 +1,286 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2013/Controls/BackstageTabItem.xaml b/Fluent.Ribbon/Themes/Office2013/Controls/BackstageTabItem.xaml similarity index 97% rename from Fluent/Themes/Office2013/Controls/BackstageTabItem.xaml rename to Fluent.Ribbon/Themes/Office2013/Controls/BackstageTabItem.xaml index 14edba809..17da951ed 100644 --- a/Fluent/Themes/Office2013/Controls/BackstageTabItem.xaml +++ b/Fluent.Ribbon/Themes/Office2013/Controls/BackstageTabItem.xaml @@ -1,89 +1,89 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2013/Controls/Button.xaml b/Fluent.Ribbon/Themes/Office2013/Controls/Button.xaml similarity index 97% rename from Fluent/Themes/Office2013/Controls/Button.xaml rename to Fluent.Ribbon/Themes/Office2013/Controls/Button.xaml index b2a6dccb6..eda382df1 100644 --- a/Fluent/Themes/Office2013/Controls/Button.xaml +++ b/Fluent.Ribbon/Themes/Office2013/Controls/Button.xaml @@ -1,249 +1,249 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Fluent/Themes/Office2013/Controls/CheckBox.xaml b/Fluent.Ribbon/Themes/Office2013/Controls/CheckBox.xaml similarity index 97% rename from Fluent/Themes/Office2013/Controls/CheckBox.xaml rename to Fluent.Ribbon/Themes/Office2013/Controls/CheckBox.xaml index 2851b3208..7261d5b77 100644 --- a/Fluent/Themes/Office2013/Controls/CheckBox.xaml +++ b/Fluent.Ribbon/Themes/Office2013/Controls/CheckBox.xaml @@ -1,241 +1,241 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Visible - - - - - - - - - - - Visible - - - - - - - True - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2013/Controls/ComboBoxItem.xaml b/Fluent.Ribbon/Themes/Office2013/Controls/ComboBoxItem.xaml similarity index 98% rename from Fluent/Themes/Office2013/Controls/ComboBoxItem.xaml rename to Fluent.Ribbon/Themes/Office2013/Controls/ComboBoxItem.xaml index d82fcfa77..bb3ba9948 100644 --- a/Fluent/Themes/Office2013/Controls/ComboBoxItem.xaml +++ b/Fluent.Ribbon/Themes/Office2013/Controls/ComboBoxItem.xaml @@ -1,134 +1,134 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2013/Controls/DropDownButton.xaml b/Fluent.Ribbon/Themes/Office2013/Controls/DropDownButton.xaml similarity index 98% rename from Fluent/Themes/Office2013/Controls/DropDownButton.xaml rename to Fluent.Ribbon/Themes/Office2013/Controls/DropDownButton.xaml index 85453b738..9ad21be95 100644 --- a/Fluent/Themes/Office2013/Controls/DropDownButton.xaml +++ b/Fluent.Ribbon/Themes/Office2013/Controls/DropDownButton.xaml @@ -1,304 +1,304 @@ - - - - - + + + - - - - - + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2013/Controls/GalleryItem.xaml b/Fluent.Ribbon/Themes/Office2013/Controls/GalleryItem.xaml similarity index 97% rename from Fluent/Themes/Office2013/Controls/GalleryItem.xaml rename to Fluent.Ribbon/Themes/Office2013/Controls/GalleryItem.xaml index 905aacbab..e23017fb0 100644 --- a/Fluent/Themes/Office2013/Controls/GalleryItem.xaml +++ b/Fluent.Ribbon/Themes/Office2013/Controls/GalleryItem.xaml @@ -1,71 +1,71 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2013/Controls/InRibbonGallery.xaml b/Fluent.Ribbon/Themes/Office2013/Controls/InRibbonGallery.xaml similarity index 98% rename from Fluent/Themes/Office2013/Controls/InRibbonGallery.xaml rename to Fluent.Ribbon/Themes/Office2013/Controls/InRibbonGallery.xaml index 59cbb7985..1bddb5599 100644 --- a/Fluent/Themes/Office2013/Controls/InRibbonGallery.xaml +++ b/Fluent.Ribbon/Themes/Office2013/Controls/InRibbonGallery.xaml @@ -1,657 +1,657 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2013/Controls/KeyTip.xaml b/Fluent.Ribbon/Themes/Office2013/Controls/KeyTip.xaml similarity index 97% rename from Fluent/Themes/Office2013/Controls/KeyTip.xaml rename to Fluent.Ribbon/Themes/Office2013/Controls/KeyTip.xaml index 4b359decf..63303cbc2 100644 --- a/Fluent/Themes/Office2013/Controls/KeyTip.xaml +++ b/Fluent.Ribbon/Themes/Office2013/Controls/KeyTip.xaml @@ -1,36 +1,36 @@ - - - + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2013/Controls/Menu.xaml b/Fluent.Ribbon/Themes/Office2013/Controls/Menu.xaml similarity index 98% rename from Fluent/Themes/Office2013/Controls/Menu.xaml rename to Fluent.Ribbon/Themes/Office2013/Controls/Menu.xaml index e0d9798b9..b768d0563 100644 --- a/Fluent/Themes/Office2013/Controls/Menu.xaml +++ b/Fluent.Ribbon/Themes/Office2013/Controls/Menu.xaml @@ -1,209 +1,209 @@ - - - - - - - - - + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2013/Controls/Ribbon.xaml b/Fluent.Ribbon/Themes/Office2013/Controls/Ribbon.xaml similarity index 98% rename from Fluent/Themes/Office2013/Controls/Ribbon.xaml rename to Fluent.Ribbon/Themes/Office2013/Controls/Ribbon.xaml index 5b8062965..aba5701e4 100644 --- a/Fluent/Themes/Office2013/Controls/Ribbon.xaml +++ b/Fluent.Ribbon/Themes/Office2013/Controls/Ribbon.xaml @@ -1,120 +1,120 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Fluent/Themes/Office2013/Controls/RibbonContextualTabGroup.xaml b/Fluent.Ribbon/Themes/Office2013/Controls/RibbonContextualTabGroup.xaml similarity index 97% rename from Fluent/Themes/Office2013/Controls/RibbonContextualTabGroup.xaml rename to Fluent.Ribbon/Themes/Office2013/Controls/RibbonContextualTabGroup.xaml index 6018339f2..d8fba900d 100644 --- a/Fluent/Themes/Office2013/Controls/RibbonContextualTabGroup.xaml +++ b/Fluent.Ribbon/Themes/Office2013/Controls/RibbonContextualTabGroup.xaml @@ -1,67 +1,67 @@ - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2013/Controls/RibbonGroupBox.xaml b/Fluent.Ribbon/Themes/Office2013/Controls/RibbonGroupBox.xaml similarity index 98% rename from Fluent/Themes/Office2013/Controls/RibbonGroupBox.xaml rename to Fluent.Ribbon/Themes/Office2013/Controls/RibbonGroupBox.xaml index bddf4cdf7..e58ee8048 100644 --- a/Fluent/Themes/Office2013/Controls/RibbonGroupBox.xaml +++ b/Fluent.Ribbon/Themes/Office2013/Controls/RibbonGroupBox.xaml @@ -1,795 +1,795 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2013/Controls/RibbonStatusBar.xaml b/Fluent.Ribbon/Themes/Office2013/Controls/RibbonStatusBar.xaml similarity index 97% rename from Fluent/Themes/Office2013/Controls/RibbonStatusBar.xaml rename to Fluent.Ribbon/Themes/Office2013/Controls/RibbonStatusBar.xaml index 72a448566..bed49b6eb 100644 --- a/Fluent/Themes/Office2013/Controls/RibbonStatusBar.xaml +++ b/Fluent.Ribbon/Themes/Office2013/Controls/RibbonStatusBar.xaml @@ -1,208 +1,208 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2013/Controls/RibbonTabItem.xaml b/Fluent.Ribbon/Themes/Office2013/Controls/RibbonTabItem.xaml similarity index 97% rename from Fluent/Themes/Office2013/Controls/RibbonTabItem.xaml rename to Fluent.Ribbon/Themes/Office2013/Controls/RibbonTabItem.xaml index 9276dd917..1be57d08e 100644 --- a/Fluent/Themes/Office2013/Controls/RibbonTabItem.xaml +++ b/Fluent.Ribbon/Themes/Office2013/Controls/RibbonTabItem.xaml @@ -1,349 +1,349 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2013/Controls/ScreenTip.xaml b/Fluent.Ribbon/Themes/Office2013/Controls/ScreenTip.xaml similarity index 98% rename from Fluent/Themes/Office2013/Controls/ScreenTip.xaml rename to Fluent.Ribbon/Themes/Office2013/Controls/ScreenTip.xaml index 9f766168f..56857206a 100644 --- a/Fluent/Themes/Office2013/Controls/ScreenTip.xaml +++ b/Fluent.Ribbon/Themes/Office2013/Controls/ScreenTip.xaml @@ -1,238 +1,238 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2013/Controls/ScrollBar.xaml b/Fluent.Ribbon/Themes/Office2013/Controls/ScrollBar.xaml similarity index 97% rename from Fluent/Themes/Office2013/Controls/ScrollBar.xaml rename to Fluent.Ribbon/Themes/Office2013/Controls/ScrollBar.xaml index a187cd4e3..3325585b7 100644 --- a/Fluent/Themes/Office2013/Controls/ScrollBar.xaml +++ b/Fluent.Ribbon/Themes/Office2013/Controls/ScrollBar.xaml @@ -1,462 +1,462 @@ - - - - - - - - - - - - - - - - - - - - - - - - - 36 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 36 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + 36 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 36 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2013/Controls/Slider.xaml b/Fluent.Ribbon/Themes/Office2013/Controls/Slider.xaml similarity index 97% rename from Fluent/Themes/Office2013/Controls/Slider.xaml rename to Fluent.Ribbon/Themes/Office2013/Controls/Slider.xaml index 88b112b0c..800615eed 100644 --- a/Fluent/Themes/Office2013/Controls/Slider.xaml +++ b/Fluent.Ribbon/Themes/Office2013/Controls/Slider.xaml @@ -1,264 +1,264 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2013/Controls/Spinner.xaml b/Fluent.Ribbon/Themes/Office2013/Controls/Spinner.xaml similarity index 98% rename from Fluent/Themes/Office2013/Controls/Spinner.xaml rename to Fluent.Ribbon/Themes/Office2013/Controls/Spinner.xaml index 64866247a..18f67f2ea 100644 --- a/Fluent/Themes/Office2013/Controls/Spinner.xaml +++ b/Fluent.Ribbon/Themes/Office2013/Controls/Spinner.xaml @@ -1,273 +1,273 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2013/Controls/SplitButton.xaml b/Fluent.Ribbon/Themes/Office2013/Controls/SplitButton.xaml similarity index 98% rename from Fluent/Themes/Office2013/Controls/SplitButton.xaml rename to Fluent.Ribbon/Themes/Office2013/Controls/SplitButton.xaml index d6c237e54..6ee7edec3 100644 --- a/Fluent/Themes/Office2013/Controls/SplitButton.xaml +++ b/Fluent.Ribbon/Themes/Office2013/Controls/SplitButton.xaml @@ -1,470 +1,470 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2013/Controls/StatusBar.xaml b/Fluent.Ribbon/Themes/Office2013/Controls/StatusBar.xaml similarity index 97% rename from Fluent/Themes/Office2013/Controls/StatusBar.xaml rename to Fluent.Ribbon/Themes/Office2013/Controls/StatusBar.xaml index 90bcfa6bd..ffe8480fb 100644 --- a/Fluent/Themes/Office2013/Controls/StatusBar.xaml +++ b/Fluent.Ribbon/Themes/Office2013/Controls/StatusBar.xaml @@ -1,63 +1,63 @@ - - - - - - + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2013/Controls/ToggleButton.xaml b/Fluent.Ribbon/Themes/Office2013/Controls/ToggleButton.xaml similarity index 97% rename from Fluent/Themes/Office2013/Controls/ToggleButton.xaml rename to Fluent.Ribbon/Themes/Office2013/Controls/ToggleButton.xaml index c733a67fd..943a558aa 100644 --- a/Fluent/Themes/Office2013/Controls/ToggleButton.xaml +++ b/Fluent.Ribbon/Themes/Office2013/Controls/ToggleButton.xaml @@ -1,204 +1,204 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Office2013/Generic.txt b/Fluent.Ribbon/Themes/Office2013/Generic.txt similarity index 97% rename from Fluent/Themes/Office2013/Generic.txt rename to Fluent.Ribbon/Themes/Office2013/Generic.txt index 93b22e479..300159c34 100644 --- a/Fluent/Themes/Office2013/Generic.txt +++ b/Fluent.Ribbon/Themes/Office2013/Generic.txt @@ -1,51 +1,51 @@ -Office2013\Colors\Colors.xaml -Office2013\Colors\ColorsWhite.xaml -Office2013\Images\Images.xaml -Generic\Controls\EmptyFocusStyle.xaml -Office2010\Controls\ApplicationMenuItem.xaml -Office2013\Controls\ApplicationMenu.xaml -Office2013\Controls\Backstage.xaml -Office2013\Controls\RibbonTabItem.xaml -Office2013\Controls\RibbonTabControl.xaml -Office2013\Controls\RibbonGroupBox.xaml -Office2013\Controls\ScreenTip.xaml -Generic\Controls\TwoLineLabel.xaml -Office2013\Controls\Button.xaml -Office2013\Controls\ToggleButton.xaml -Office2013\Controls\DropDownButton.xaml -Office2013\Controls\SplitButton.xaml -Office2013\Controls\CheckBox.xaml -Generic\Controls\RadioButton.xaml -Generic\Controls\GalleryGroupContainer.xaml -Generic\Controls\RibbonScrollViewer.xaml -Office2013\Controls\RibbonContextualTabGroup.xaml -Generic\Controls\RibbonTitleBar.xaml -Office2013\Controls\KeyTip.xaml -Office2013\Controls\QuickAccessToolBar.xaml -Office2013\Controls\Menu.xaml -Office2013\Controls\MenuItem.xaml -Generic\Controls\MenuSeparator.xaml -Generic\Controls\RibbonMenu.xaml -Office2013\Controls\Gallery.xaml -Office2013\Controls\GalleryItem.xaml -Office2013\Controls\ComboBoxItem.xaml -Office2013\Controls\InRibbonGallery.xaml -Office2013\Controls\BackstageTabItem.xaml -Office2013\Controls\BackstageControls.xaml -Office2013\Controls\BackstageTabControl.xaml -Office2013\Controls\RibbonSeparator.xaml -Generic\Controls\RibbonToolBar.xaml -Generic\Controls\RibbonToolBarControlGroup.xaml -Office2013\Controls\StatusBar.xaml -Office2013\Controls\ScrollBar.xaml -Generic\Controls\RibbonTextBox.xaml -Generic\Controls\TextBox.xaml -Office2013\Controls\Spinner.xaml -Office2013\Controls\ComboBox.xaml -Office2013\Controls\Ribbon.xaml -Office2010\Controls\ColorGallery.xaml -Generic\Controls\SeparatorTabItem.xaml -Office2013\Controls\RibbonStatusBar.xaml -Office2013\Controls\Slider.xaml -Office2013\RibbonWindow.xaml +Office2013\Colors\Colors.xaml +Office2013\Colors\ColorsWhite.xaml +Office2013\Images\Images.xaml +Generic\Controls\EmptyFocusStyle.xaml +Office2010\Controls\ApplicationMenuItem.xaml +Office2013\Controls\ApplicationMenu.xaml +Office2013\Controls\Backstage.xaml +Office2013\Controls\RibbonTabItem.xaml +Office2013\Controls\RibbonTabControl.xaml +Office2013\Controls\RibbonGroupBox.xaml +Office2013\Controls\ScreenTip.xaml +Generic\Controls\TwoLineLabel.xaml +Office2013\Controls\Button.xaml +Office2013\Controls\ToggleButton.xaml +Office2013\Controls\DropDownButton.xaml +Office2013\Controls\SplitButton.xaml +Office2013\Controls\CheckBox.xaml +Generic\Controls\RadioButton.xaml +Generic\Controls\GalleryGroupContainer.xaml +Generic\Controls\RibbonScrollViewer.xaml +Office2013\Controls\RibbonContextualTabGroup.xaml +Generic\Controls\RibbonTitleBar.xaml +Office2013\Controls\KeyTip.xaml +Office2013\Controls\QuickAccessToolBar.xaml +Office2013\Controls\Menu.xaml +Office2013\Controls\MenuItem.xaml +Generic\Controls\MenuSeparator.xaml +Generic\Controls\RibbonMenu.xaml +Office2013\Controls\Gallery.xaml +Office2013\Controls\GalleryItem.xaml +Office2013\Controls\ComboBoxItem.xaml +Office2013\Controls\InRibbonGallery.xaml +Office2013\Controls\BackstageTabItem.xaml +Office2013\Controls\BackstageControls.xaml +Office2013\Controls\BackstageTabControl.xaml +Office2013\Controls\RibbonSeparator.xaml +Generic\Controls\RibbonToolBar.xaml +Generic\Controls\RibbonToolBarControlGroup.xaml +Office2013\Controls\StatusBar.xaml +Office2013\Controls\ScrollBar.xaml +Generic\Controls\RibbonTextBox.xaml +Generic\Controls\TextBox.xaml +Office2013\Controls\Spinner.xaml +Office2013\Controls\ComboBox.xaml +Office2013\Controls\Ribbon.xaml +Office2010\Controls\ColorGallery.xaml +Generic\Controls\SeparatorTabItem.xaml +Office2013\Controls\RibbonStatusBar.xaml +Office2013\Controls\Slider.xaml +Office2013\RibbonWindow.xaml Generic\Common.xaml \ No newline at end of file diff --git a/Fluent.Ribbon/Themes/Office2013/Generic.xaml b/Fluent.Ribbon/Themes/Office2013/Generic.xaml new file mode 100644 index 000000000..b5c807569 --- /dev/null +++ b/Fluent.Ribbon/Themes/Office2013/Generic.xaml @@ -0,0 +1,6295 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #FF000000 + #FFFFFFFF + #FFCCCCCC + #FFD8D8D9 + #00FFFFFF + + #2B579A + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Visible + + + + + + + + + + + Visible + + + + + + + True + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 36 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 36 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fluent/Themes/Windows8/Colors/Colors.xaml b/Fluent.Ribbon/Themes/Windows8/Colors/Colors.xaml similarity index 100% rename from Fluent/Themes/Windows8/Colors/Colors.xaml rename to Fluent.Ribbon/Themes/Windows8/Colors/Colors.xaml diff --git a/Fluent/Themes/Windows8/Colors/ColorsSilver.xaml b/Fluent.Ribbon/Themes/Windows8/Colors/ColorsSilver.xaml similarity index 100% rename from Fluent/Themes/Windows8/Colors/ColorsSilver.xaml rename to Fluent.Ribbon/Themes/Windows8/Colors/ColorsSilver.xaml diff --git a/Fluent/Themes/Windows8/Controls/ApplicationMenu.xaml b/Fluent.Ribbon/Themes/Windows8/Controls/ApplicationMenu.xaml similarity index 100% rename from Fluent/Themes/Windows8/Controls/ApplicationMenu.xaml rename to Fluent.Ribbon/Themes/Windows8/Controls/ApplicationMenu.xaml diff --git a/Fluent/Themes/Windows8/Controls/ApplicationMenuItem.xaml b/Fluent.Ribbon/Themes/Windows8/Controls/ApplicationMenuItem.xaml similarity index 100% rename from Fluent/Themes/Windows8/Controls/ApplicationMenuItem.xaml rename to Fluent.Ribbon/Themes/Windows8/Controls/ApplicationMenuItem.xaml diff --git a/Fluent/Themes/Windows8/Controls/Backstage.xaml b/Fluent.Ribbon/Themes/Windows8/Controls/Backstage.xaml similarity index 100% rename from Fluent/Themes/Windows8/Controls/Backstage.xaml rename to Fluent.Ribbon/Themes/Windows8/Controls/Backstage.xaml diff --git a/Fluent/Themes/Windows8/Controls/BackstageControls.xaml b/Fluent.Ribbon/Themes/Windows8/Controls/BackstageControls.xaml similarity index 100% rename from Fluent/Themes/Windows8/Controls/BackstageControls.xaml rename to Fluent.Ribbon/Themes/Windows8/Controls/BackstageControls.xaml diff --git a/Fluent/Themes/Windows8/Controls/BackstageTabControl.xaml b/Fluent.Ribbon/Themes/Windows8/Controls/BackstageTabControl.xaml similarity index 100% rename from Fluent/Themes/Windows8/Controls/BackstageTabControl.xaml rename to Fluent.Ribbon/Themes/Windows8/Controls/BackstageTabControl.xaml diff --git a/Fluent/Themes/Windows8/Controls/BackstageTabItem.xaml b/Fluent.Ribbon/Themes/Windows8/Controls/BackstageTabItem.xaml similarity index 100% rename from Fluent/Themes/Windows8/Controls/BackstageTabItem.xaml rename to Fluent.Ribbon/Themes/Windows8/Controls/BackstageTabItem.xaml diff --git a/Fluent/Themes/Windows8/Controls/Button.xaml b/Fluent.Ribbon/Themes/Windows8/Controls/Button.xaml similarity index 100% rename from Fluent/Themes/Windows8/Controls/Button.xaml rename to Fluent.Ribbon/Themes/Windows8/Controls/Button.xaml diff --git a/Fluent/Themes/Windows8/Controls/CheckBox.xaml b/Fluent.Ribbon/Themes/Windows8/Controls/CheckBox.xaml similarity index 100% rename from Fluent/Themes/Windows8/Controls/CheckBox.xaml rename to Fluent.Ribbon/Themes/Windows8/Controls/CheckBox.xaml diff --git a/Fluent/Themes/Windows8/Controls/ColorGallery.xaml b/Fluent.Ribbon/Themes/Windows8/Controls/ColorGallery.xaml similarity index 100% rename from Fluent/Themes/Windows8/Controls/ColorGallery.xaml rename to Fluent.Ribbon/Themes/Windows8/Controls/ColorGallery.xaml diff --git a/Fluent/Themes/Windows8/Controls/ComboBox.xaml b/Fluent.Ribbon/Themes/Windows8/Controls/ComboBox.xaml similarity index 100% rename from Fluent/Themes/Windows8/Controls/ComboBox.xaml rename to Fluent.Ribbon/Themes/Windows8/Controls/ComboBox.xaml diff --git a/Fluent/Themes/Windows8/Controls/ComboBoxItem.xaml b/Fluent.Ribbon/Themes/Windows8/Controls/ComboBoxItem.xaml similarity index 100% rename from Fluent/Themes/Windows8/Controls/ComboBoxItem.xaml rename to Fluent.Ribbon/Themes/Windows8/Controls/ComboBoxItem.xaml diff --git a/Fluent/Themes/Windows8/Controls/DropDownButton.xaml b/Fluent.Ribbon/Themes/Windows8/Controls/DropDownButton.xaml similarity index 100% rename from Fluent/Themes/Windows8/Controls/DropDownButton.xaml rename to Fluent.Ribbon/Themes/Windows8/Controls/DropDownButton.xaml diff --git a/Fluent/Themes/Windows8/Controls/Gallery.xaml b/Fluent.Ribbon/Themes/Windows8/Controls/Gallery.xaml similarity index 100% rename from Fluent/Themes/Windows8/Controls/Gallery.xaml rename to Fluent.Ribbon/Themes/Windows8/Controls/Gallery.xaml diff --git a/Fluent/Themes/Windows8/Controls/GalleryItem.xaml b/Fluent.Ribbon/Themes/Windows8/Controls/GalleryItem.xaml similarity index 100% rename from Fluent/Themes/Windows8/Controls/GalleryItem.xaml rename to Fluent.Ribbon/Themes/Windows8/Controls/GalleryItem.xaml diff --git a/Fluent/Themes/Windows8/Controls/InRibbonGallery.xaml b/Fluent.Ribbon/Themes/Windows8/Controls/InRibbonGallery.xaml similarity index 100% rename from Fluent/Themes/Windows8/Controls/InRibbonGallery.xaml rename to Fluent.Ribbon/Themes/Windows8/Controls/InRibbonGallery.xaml diff --git a/Fluent/Themes/Windows8/Controls/Menu.xaml b/Fluent.Ribbon/Themes/Windows8/Controls/Menu.xaml similarity index 100% rename from Fluent/Themes/Windows8/Controls/Menu.xaml rename to Fluent.Ribbon/Themes/Windows8/Controls/Menu.xaml diff --git a/Fluent/Themes/Windows8/Controls/MenuItem.xaml b/Fluent.Ribbon/Themes/Windows8/Controls/MenuItem.xaml similarity index 100% rename from Fluent/Themes/Windows8/Controls/MenuItem.xaml rename to Fluent.Ribbon/Themes/Windows8/Controls/MenuItem.xaml diff --git a/Fluent/Themes/Windows8/Controls/QuickAccessToolbar.xaml b/Fluent.Ribbon/Themes/Windows8/Controls/QuickAccessToolbar.xaml similarity index 100% rename from Fluent/Themes/Windows8/Controls/QuickAccessToolbar.xaml rename to Fluent.Ribbon/Themes/Windows8/Controls/QuickAccessToolbar.xaml diff --git a/Fluent/Themes/Windows8/Controls/Ribbon.xaml b/Fluent.Ribbon/Themes/Windows8/Controls/Ribbon.xaml similarity index 100% rename from Fluent/Themes/Windows8/Controls/Ribbon.xaml rename to Fluent.Ribbon/Themes/Windows8/Controls/Ribbon.xaml diff --git a/Fluent/Themes/Windows8/Controls/RibbonContextualTabGroup.xaml b/Fluent.Ribbon/Themes/Windows8/Controls/RibbonContextualTabGroup.xaml similarity index 100% rename from Fluent/Themes/Windows8/Controls/RibbonContextualTabGroup.xaml rename to Fluent.Ribbon/Themes/Windows8/Controls/RibbonContextualTabGroup.xaml diff --git a/Fluent/Themes/Windows8/Controls/RibbonGroupBox.xaml b/Fluent.Ribbon/Themes/Windows8/Controls/RibbonGroupBox.xaml similarity index 100% rename from Fluent/Themes/Windows8/Controls/RibbonGroupBox.xaml rename to Fluent.Ribbon/Themes/Windows8/Controls/RibbonGroupBox.xaml diff --git a/Fluent/Themes/Windows8/Controls/RibbonSeparator.xaml b/Fluent.Ribbon/Themes/Windows8/Controls/RibbonSeparator.xaml similarity index 100% rename from Fluent/Themes/Windows8/Controls/RibbonSeparator.xaml rename to Fluent.Ribbon/Themes/Windows8/Controls/RibbonSeparator.xaml diff --git a/Fluent/Themes/Windows8/Controls/RibbonStatusBar.xaml b/Fluent.Ribbon/Themes/Windows8/Controls/RibbonStatusBar.xaml similarity index 100% rename from Fluent/Themes/Windows8/Controls/RibbonStatusBar.xaml rename to Fluent.Ribbon/Themes/Windows8/Controls/RibbonStatusBar.xaml diff --git a/Fluent/Themes/Windows8/Controls/RibbonTabControl.xaml b/Fluent.Ribbon/Themes/Windows8/Controls/RibbonTabControl.xaml similarity index 100% rename from Fluent/Themes/Windows8/Controls/RibbonTabControl.xaml rename to Fluent.Ribbon/Themes/Windows8/Controls/RibbonTabControl.xaml diff --git a/Fluent/Themes/Windows8/Controls/RibbonTabItem.xaml b/Fluent.Ribbon/Themes/Windows8/Controls/RibbonTabItem.xaml similarity index 100% rename from Fluent/Themes/Windows8/Controls/RibbonTabItem.xaml rename to Fluent.Ribbon/Themes/Windows8/Controls/RibbonTabItem.xaml diff --git a/Fluent/Themes/Windows8/Controls/ScrollBar.xaml b/Fluent.Ribbon/Themes/Windows8/Controls/ScrollBar.xaml similarity index 100% rename from Fluent/Themes/Windows8/Controls/ScrollBar.xaml rename to Fluent.Ribbon/Themes/Windows8/Controls/ScrollBar.xaml diff --git a/Fluent/Themes/Windows8/Controls/Slider.xaml b/Fluent.Ribbon/Themes/Windows8/Controls/Slider.xaml similarity index 100% rename from Fluent/Themes/Windows8/Controls/Slider.xaml rename to Fluent.Ribbon/Themes/Windows8/Controls/Slider.xaml diff --git a/Fluent/Themes/Windows8/Controls/Spinner.xaml b/Fluent.Ribbon/Themes/Windows8/Controls/Spinner.xaml similarity index 100% rename from Fluent/Themes/Windows8/Controls/Spinner.xaml rename to Fluent.Ribbon/Themes/Windows8/Controls/Spinner.xaml diff --git a/Fluent/Themes/Windows8/Controls/SplitButton.xaml b/Fluent.Ribbon/Themes/Windows8/Controls/SplitButton.xaml similarity index 100% rename from Fluent/Themes/Windows8/Controls/SplitButton.xaml rename to Fluent.Ribbon/Themes/Windows8/Controls/SplitButton.xaml diff --git a/Fluent/Themes/Windows8/Controls/StatusBar.xaml b/Fluent.Ribbon/Themes/Windows8/Controls/StatusBar.xaml similarity index 100% rename from Fluent/Themes/Windows8/Controls/StatusBar.xaml rename to Fluent.Ribbon/Themes/Windows8/Controls/StatusBar.xaml diff --git a/Fluent/Themes/Windows8/Controls/ToggleButton.xaml b/Fluent.Ribbon/Themes/Windows8/Controls/ToggleButton.xaml similarity index 100% rename from Fluent/Themes/Windows8/Controls/ToggleButton.xaml rename to Fluent.Ribbon/Themes/Windows8/Controls/ToggleButton.xaml diff --git a/Fluent/Themes/Windows8/Generic.txt b/Fluent.Ribbon/Themes/Windows8/Generic.txt similarity index 100% rename from Fluent/Themes/Windows8/Generic.txt rename to Fluent.Ribbon/Themes/Windows8/Generic.txt diff --git a/Fluent.Ribbon/Themes/Windows8/Generic.xaml b/Fluent.Ribbon/Themes/Windows8/Generic.xaml new file mode 100644 index 000000000..38d79232e --- /dev/null +++ b/Fluent.Ribbon/Themes/Windows8/Generic.xaml @@ -0,0 +1,6380 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Visible + + + + + + + + + + + Visible + + + + + + + True + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Fluent.Ribbon/Themes/Windows8/RibbonWindow.xaml b/Fluent.Ribbon/Themes/Windows8/RibbonWindow.xaml index c0dde2295..8712638ba 100644 --- a/Fluent.Ribbon/Themes/Windows8/RibbonWindow.xaml +++ b/Fluent.Ribbon/Themes/Windows8/RibbonWindow.xaml @@ -22,6 +22,7 @@ + + @@ -380,6 +382,7 @@ + + From 5760bc6453b0dc51a29a52ac7d6062676ed6c8d1 Mon Sep 17 00:00:00 2001 From: Bastian Schmidt Date: Sun, 13 Dec 2015 23:02:40 +0100 Subject: [PATCH 019/186] - Fixes #210 by using WindowCaptionButtonsLocation - Updating ignores and deleting accidentally added generic.xaml files --- .gitignore | 4 +- Fluent.Ribbon.nuspec | 2 +- Fluent.Ribbon/Fluent.Ribbon.NET 4.0.csproj | 4 +- Fluent.Ribbon/Fluent.Ribbon.NET 4.5.csproj | 4 +- .../Themes/Office2010/Controls/Ribbon.xaml | 11 +- Fluent.Ribbon/Themes/Office2010/Generic.xaml | 7705 ----------------- .../Themes/Office2010/RibbonWindow.xaml | 3 +- .../Themes/Office2013/Controls/Ribbon.xaml | 11 +- Fluent.Ribbon/Themes/Office2013/Generic.xaml | 6295 -------------- .../Themes/Windows8/Controls/Ribbon.xaml | 11 +- Fluent.Ribbon/Themes/Windows8/Generic.xaml | 6380 -------------- .../Themes/Windows8/RibbonWindow.xaml | 3 +- Fluent.Ribbon/packages.config | 2 +- 13 files changed, 40 insertions(+), 20395 deletions(-) delete mode 100644 Fluent.Ribbon/Themes/Office2010/Generic.xaml delete mode 100644 Fluent.Ribbon/Themes/Office2013/Generic.xaml delete mode 100644 Fluent.Ribbon/Themes/Windows8/Generic.xaml diff --git a/.gitignore b/.gitignore index c5d44cb84..2b2b985db 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,6 @@ publish/ *.nupkg # Generated files -Fluent/Themes/**/Generic.xaml -!Fluent/Themes/Generic.xaml +Fluent.Ribbon/Themes/**/Generic.xaml +!Fluent.Ribbon/Themes/Generic.xaml packages/ diff --git a/Fluent.Ribbon.nuspec b/Fluent.Ribbon.nuspec index 499309315..cff8c02cb 100644 --- a/Fluent.Ribbon.nuspec +++ b/Fluent.Ribbon.nuspec @@ -14,7 +14,7 @@ false ribbon fluent ribbonwindow office2010 office2013 windows8 backstage - + diff --git a/Fluent.Ribbon/Fluent.Ribbon.NET 4.0.csproj b/Fluent.Ribbon/Fluent.Ribbon.NET 4.0.csproj index 4ed1382d8..cad829a18 100644 --- a/Fluent.Ribbon/Fluent.Ribbon.NET 4.0.csproj +++ b/Fluent.Ribbon/Fluent.Ribbon.NET 4.0.csproj @@ -53,7 +53,7 @@ - ..\packages\ControlzEx.2.0.0-dev7\lib\net40\ControlzEx.dll + ..\packages\ControlzEx.2.0.0-dev9\lib\net40\ControlzEx.dll True @@ -61,7 +61,7 @@ - ..\packages\ControlzEx.2.0.0-dev7\lib\net40\System.Windows.Interactivity.dll + ..\packages\ControlzEx.2.0.0-dev9\lib\net40\System.Windows.Interactivity.dll True diff --git a/Fluent.Ribbon/Fluent.Ribbon.NET 4.5.csproj b/Fluent.Ribbon/Fluent.Ribbon.NET 4.5.csproj index ef65fdb34..2caa08290 100644 --- a/Fluent.Ribbon/Fluent.Ribbon.NET 4.5.csproj +++ b/Fluent.Ribbon/Fluent.Ribbon.NET 4.5.csproj @@ -53,7 +53,7 @@ - ..\packages\ControlzEx.2.0.0-dev7\lib\net45\ControlzEx.dll + ..\packages\ControlzEx.2.0.0-dev9\lib\net45\ControlzEx.dll True @@ -61,7 +61,7 @@ - ..\packages\ControlzEx.2.0.0-dev7\lib\net45\System.Windows.Interactivity.dll + ..\packages\ControlzEx.2.0.0-dev9\lib\net45\System.Windows.Interactivity.dll True diff --git a/Fluent.Ribbon/Themes/Office2010/Controls/Ribbon.xaml b/Fluent.Ribbon/Themes/Office2010/Controls/Ribbon.xaml index 4b571052d..6fc594655 100644 --- a/Fluent.Ribbon/Themes/Office2010/Controls/Ribbon.xaml +++ b/Fluent.Ribbon/Themes/Office2010/Controls/Ribbon.xaml @@ -5,6 +5,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:Converters="clr-namespace:Fluent.Converters" xmlns:internal="clr-namespace:Fluent.Internal" + xmlns:ControlzEx="urn:controlzex" mc:Ignorable="d"> @@ -18,10 +19,18 @@ + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #D8FFFFFF - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 36 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 36 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 36 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 36 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Visible - - - - - - - - - - - Visible - - - - - - - True - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 36 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 36 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Visible - - - - - - - - - - - Visible - - - - - - - True - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Fluent.Ribbon/Themes/Windows8/RibbonWindow.xaml b/Fluent.Ribbon/Themes/Windows8/RibbonWindow.xaml index 606b48447..3e027b8f6 100644 --- a/Fluent.Ribbon/Themes/Windows8/RibbonWindow.xaml +++ b/Fluent.Ribbon/Themes/Windows8/RibbonWindow.xaml @@ -712,6 +712,11 @@ + + + \ No newline at end of file From 0bd0b5f139c6e05e71721564fa2d82a9e9763dba Mon Sep 17 00:00:00 2001 From: Bastian Schmidt Date: Tue, 15 Dec 2015 17:36:28 +0100 Subject: [PATCH 029/186] Setting CornerRadius to 0 in Office 2010 theme when window is maximized #10 --- Fluent.Ribbon/Themes/Office2010/RibbonWindow.xaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Fluent.Ribbon/Themes/Office2010/RibbonWindow.xaml b/Fluent.Ribbon/Themes/Office2010/RibbonWindow.xaml index ff8e7845c..4050ddb81 100644 --- a/Fluent.Ribbon/Themes/Office2010/RibbonWindow.xaml +++ b/Fluent.Ribbon/Themes/Office2010/RibbonWindow.xaml @@ -748,6 +748,11 @@ + + + Date: Tue, 15 Dec 2015 17:40:46 +0100 Subject: [PATCH 030/186] Fixes #129 by removing bug workaround for WindowChrome --- Fluent.Ribbon/Themes/Office2010/RibbonWindow.xaml | 3 +-- Fluent.Ribbon/Themes/Office2013/RibbonWindow.xaml | 2 +- Fluent.Ribbon/Themes/Windows8/RibbonWindow.xaml | 3 +-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Fluent.Ribbon/Themes/Office2010/RibbonWindow.xaml b/Fluent.Ribbon/Themes/Office2010/RibbonWindow.xaml index 4050ddb81..6c4a5f5c4 100644 --- a/Fluent.Ribbon/Themes/Office2010/RibbonWindow.xaml +++ b/Fluent.Ribbon/Themes/Office2010/RibbonWindow.xaml @@ -757,9 +757,8 @@ Value="False"> - + Value="0" /> diff --git a/Fluent.Ribbon/Themes/Office2013/RibbonWindow.xaml b/Fluent.Ribbon/Themes/Office2013/RibbonWindow.xaml index d9f3ed755..93cc67b69 100644 --- a/Fluent.Ribbon/Themes/Office2013/RibbonWindow.xaml +++ b/Fluent.Ribbon/Themes/Office2013/RibbonWindow.xaml @@ -282,7 +282,7 @@ Value="0" /> + Value="0" /> diff --git a/Fluent.Ribbon/Themes/Windows8/RibbonWindow.xaml b/Fluent.Ribbon/Themes/Windows8/RibbonWindow.xaml index 3e027b8f6..b997c9f22 100644 --- a/Fluent.Ribbon/Themes/Windows8/RibbonWindow.xaml +++ b/Fluent.Ribbon/Themes/Windows8/RibbonWindow.xaml @@ -703,9 +703,8 @@ Value="False"> - + Value="0" /> From 24ae08ceb733a21c2f738a6726f812ba67ffedb2 Mon Sep 17 00:00:00 2001 From: Bastian Schmidt Date: Tue, 15 Dec 2015 17:45:03 +0100 Subject: [PATCH 031/186] Adding .editorconfig --- .editorconfig | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..8c1b229e5 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,20 @@ +; Top-most http://editorconfig.org/ file +root = true + +[*] +end_of_line = CRLF + +; 4-column tab indentation +[*.cs] +indent_style = space +indent_size = 4 + +; 4-column tab indentation +[*.xaml] +indent_style = space +indent_size = 4 + +; 4-column tab indentation +[*.xml] +indent_style = space +indent_size = 4 From 1adfe021754b916a1d3edf0d18f821f5131a7e91 Mon Sep 17 00:00:00 2001 From: Bastian Schmidt Date: Tue, 15 Dec 2015 17:48:48 +0100 Subject: [PATCH 032/186] Changing spacing for .md files --- .editorconfig | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.editorconfig b/.editorconfig index 8c1b229e5..5fbda1a82 100644 --- a/.editorconfig +++ b/.editorconfig @@ -4,17 +4,18 @@ root = true [*] end_of_line = CRLF -; 4-column tab indentation [*.cs] indent_style = space indent_size = 4 -; 4-column tab indentation [*.xaml] indent_style = space indent_size = 4 -; 4-column tab indentation [*.xml] indent_style = space indent_size = 4 + +[*.md] +indent_style = space +indent_size = 2 \ No newline at end of file From 1c0cb163d5179cbc4f641329de7e3b2a152f894c Mon Sep 17 00:00:00 2001 From: Bastian Schmidt Date: Tue, 15 Dec 2015 17:51:54 +0100 Subject: [PATCH 033/186] Adding development requirements to Readme.md --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 852a766a3..af0d6f5b9 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Fluent Ribbon Control Suite is a library that implements an Office-like user int ### Contact -If you wish to contact me (batzen) directly please use twitter https://twitter.com/batzendev. +If you wish to contact me (batzen) directly please use [twitter](https://twitter.com/batzendev) or [gitter](https://gitter.im/batzen). ### Contributing @@ -32,6 +32,11 @@ If you wish to contact me (batzen) directly please use twitter https://twitter.c * Help us updating the documentation and walkthrough * Help us writing a changelog/release notes for the next version +### Development requirements +* Visual Studio 2015 +* Optional but recommended + * Editorconfig extension for Visual Studio 2015 + ### Settings that should be used * Visual Studio settings which should be used * All languages From e68014d99b687f73c1fe5fc3dda57f7306879dcb Mon Sep 17 00:00:00 2001 From: Bastian Schmidt Date: Tue, 15 Dec 2015 17:58:19 +0100 Subject: [PATCH 034/186] Improving editorconfig --- .editorconfig | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/.editorconfig b/.editorconfig index 5fbda1a82..96bbb19f8 100644 --- a/.editorconfig +++ b/.editorconfig @@ -4,18 +4,10 @@ root = true [*] end_of_line = CRLF -[*.cs] +[*.{cs,xaml,xml}] indent_style = space indent_size = 4 -[*.xaml] -indent_style = space -indent_size = 4 - -[*.xml] -indent_style = space -indent_size = 4 - -[*.md] +[*.{md,yml}] indent_style = space indent_size = 2 \ No newline at end of file From 49ac1755e2b7eebf1fa40c4adbd1a6d2e63d6d98 Mon Sep 17 00:00:00 2001 From: Bastian Schmidt Date: Wed, 16 Dec 2015 19:10:40 +0100 Subject: [PATCH 035/186] ReSharper: Re-Enabling support for C# 6.0 --- .../Fluent.Ribbon.Showcase.NET 4.0.csproj.DotSettings | 2 -- .../Fluent.Ribbon.Showcase.NET 4.5.csproj.DotSettings | 2 -- Fluent.Ribbon/Fluent.Ribbon.NET 4.0.csproj.DotSettings | 2 -- Fluent.Ribbon/Fluent.Ribbon.NET 4.5.csproj.DotSettings | 2 -- 4 files changed, 8 deletions(-) delete mode 100644 Fluent.Ribbon.Showcase/Fluent.Ribbon.Showcase.NET 4.0.csproj.DotSettings delete mode 100644 Fluent.Ribbon.Showcase/Fluent.Ribbon.Showcase.NET 4.5.csproj.DotSettings delete mode 100644 Fluent.Ribbon/Fluent.Ribbon.NET 4.0.csproj.DotSettings delete mode 100644 Fluent.Ribbon/Fluent.Ribbon.NET 4.5.csproj.DotSettings diff --git a/Fluent.Ribbon.Showcase/Fluent.Ribbon.Showcase.NET 4.0.csproj.DotSettings b/Fluent.Ribbon.Showcase/Fluent.Ribbon.Showcase.NET 4.0.csproj.DotSettings deleted file mode 100644 index 662f95686..000000000 --- a/Fluent.Ribbon.Showcase/Fluent.Ribbon.Showcase.NET 4.0.csproj.DotSettings +++ /dev/null @@ -1,2 +0,0 @@ - - CSharp50 \ No newline at end of file diff --git a/Fluent.Ribbon.Showcase/Fluent.Ribbon.Showcase.NET 4.5.csproj.DotSettings b/Fluent.Ribbon.Showcase/Fluent.Ribbon.Showcase.NET 4.5.csproj.DotSettings deleted file mode 100644 index 662f95686..000000000 --- a/Fluent.Ribbon.Showcase/Fluent.Ribbon.Showcase.NET 4.5.csproj.DotSettings +++ /dev/null @@ -1,2 +0,0 @@ - - CSharp50 \ No newline at end of file diff --git a/Fluent.Ribbon/Fluent.Ribbon.NET 4.0.csproj.DotSettings b/Fluent.Ribbon/Fluent.Ribbon.NET 4.0.csproj.DotSettings deleted file mode 100644 index 662f95686..000000000 --- a/Fluent.Ribbon/Fluent.Ribbon.NET 4.0.csproj.DotSettings +++ /dev/null @@ -1,2 +0,0 @@ - - CSharp50 \ No newline at end of file diff --git a/Fluent.Ribbon/Fluent.Ribbon.NET 4.5.csproj.DotSettings b/Fluent.Ribbon/Fluent.Ribbon.NET 4.5.csproj.DotSettings deleted file mode 100644 index 662f95686..000000000 --- a/Fluent.Ribbon/Fluent.Ribbon.NET 4.5.csproj.DotSettings +++ /dev/null @@ -1,2 +0,0 @@ - - CSharp50 \ No newline at end of file From 81cd23c5946cb4a3d12b1a2ebd14c9c644223f67 Mon Sep 17 00:00:00 2001 From: Bastian Schmidt Date: Wed, 16 Dec 2015 19:11:07 +0100 Subject: [PATCH 036/186] Adding Readme.md to solution items --- Fluent.Ribbon.sln | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/Fluent.Ribbon.sln b/Fluent.Ribbon.sln index 5113d3349..313f7d0df 100644 --- a/Fluent.Ribbon.sln +++ b/Fluent.Ribbon.sln @@ -7,14 +7,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fluent.Ribbon.Showcase.NET EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fluent.Ribbon.NET 4.5", "Fluent.Ribbon\Fluent.Ribbon.NET 4.5.csproj", "{4C92FCF4-3561-499F-BC5B-F2F089863047}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{B3EE3E07-9FD5-4282-9743-AC457EB45BBA}" - ProjectSection(SolutionItems) = preProject - Changelog.md = Changelog.md - Shared\GlobalAssemblyInfo.cs = Shared\GlobalAssemblyInfo.cs - License.txt = License.txt - ReleaseNotes.md = ReleaseNotes.md - EndProjectSection -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fluent.Ribbon.NET 4.0", "Fluent.Ribbon\Fluent.Ribbon.NET 4.0.csproj", "{281095D8-D8B3-4A7F-8896-646483FB685C}" ProjectSection(ProjectDependencies) = postProject {4C92FCF4-3561-499F-BC5B-F2F089863047} = {4C92FCF4-3561-499F-BC5B-F2F089863047} @@ -36,6 +28,15 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{CDBFA905 Fluent.Ribbon.nuspec = Fluent.Ribbon.nuspec EndProjectSection EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{C6ADD536-3DB2-4418-AED6-98AB991D1B77}" + ProjectSection(SolutionItems) = preProject + Changelog.md = Changelog.md + Shared\GlobalAssemblyInfo.cs = Shared\GlobalAssemblyInfo.cs + License.txt = License.txt + README.md = README.md + ReleaseNotes.md = ReleaseNotes.md + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU From 8aa811e93950bca8de4fe58039a3b1978271e61d Mon Sep 17 00:00:00 2001 From: Bastian Schmidt Date: Wed, 16 Dec 2015 19:11:39 +0100 Subject: [PATCH 037/186] Fixes #223 by adding check for ResizeMode --- .../Controls/RibbonContextualTabGroup.cs | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/Fluent.Ribbon/Controls/RibbonContextualTabGroup.cs b/Fluent.Ribbon/Controls/RibbonContextualTabGroup.cs index ba3e33788..ee34f5eff 100644 --- a/Fluent.Ribbon/Controls/RibbonContextualTabGroup.cs +++ b/Fluent.Ribbon/Controls/RibbonContextualTabGroup.cs @@ -128,7 +128,7 @@ static RibbonContextualTabGroup() } // Coerce object style - static object OnCoerceStyle(DependencyObject d, object basevalue) + private static object OnCoerceStyle(DependencyObject d, object basevalue) { if (basevalue == null) { @@ -304,14 +304,11 @@ protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e) if (e.ClickCount == 1 && firstVisibleItem != null) { - if (firstVisibleItem.TabControlParent != null) - { - var currentSelectedItem = firstVisibleItem.TabControlParent.SelectedItem as RibbonTabItem; + var currentSelectedItem = firstVisibleItem.TabControlParent?.SelectedItem as RibbonTabItem; - if (currentSelectedItem != null) - { - currentSelectedItem.IsSelected = false; - } + if (currentSelectedItem != null) + { + currentSelectedItem.IsSelected = false; } e.Handled = true; @@ -343,6 +340,11 @@ protected override void OnMouseDoubleClick(MouseButtonEventArgs e) return; } + if (this.parentWidow.ResizeMode == ResizeMode.NoResize) + { + return; + } + this.parentWidow.WindowState = this.parentWidow.WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized; @@ -363,4 +365,4 @@ private void OnParentWindowStateChanged(object sender, EventArgs e) this.IsWindowMaximized = this.parentWidow.WindowState == WindowState.Maximized; } } -} +} \ No newline at end of file From 6ce4a9909b2042781182eae04f52fd27cab443bb Mon Sep 17 00:00:00 2001 From: Bastian Schmidt Date: Wed, 16 Dec 2015 19:11:56 +0100 Subject: [PATCH 038/186] Updating nuget.exe --- Tools/nuget.exe | Bin 1664512 -> 3787952 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Tools/nuget.exe b/Tools/nuget.exe index 9ca66594f912a1fe7aec510819006fb1a80bc1a9..9f8781de0db12095b1bf32b209c112a2d85704d2 100644 GIT binary patch literal 3787952 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

FHw2-X5#s|1_~A<*NiJE%yx`!>gEw0Ff^$2eOa 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%0Ak+vHhSMj!fup? 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%0A& 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⪼0 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

{(!)DmKF;tpHeRSfK9{myD?fWZwhWDr?^Vatue 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

=hkZO)DL@t6zmH76i?wbMqH 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 z zOvlS@B265}r4F>9;!BE14h$Iwnlx()aU z5~Y4+bp(y31w8Y}ERNIJLAfJE_9 zS*?Vm-CYBT+)Y=TjZ|6fG*@l42a=A-Pau7Tqow)_-)VzLYKLzv^)`~NhC-scDP7$P z=^h-m%EvePAYGLRTMkLr{Er(cU9E$(9Y?zQM5JGg`T zsGjDkqplWdG$e{~N8JS}4~L_k5Vk?2ts=cA(q|(5Dv~u=+t*g5L8^tLt!iPUj;cGP zK{z_9vG~3hhpp!0Tj#Z9khGs)f<$GZty+NAemBnAszdnJ(r-peDE|<*g{WjCTQ!2D zZE0&H)Ul9s?i@gRsJRkq3?#~(33abXvqYLtdfaL}0ZGT-zaUXd(pLTOi*B*2segm% zGP4)bK^zyWZ}EMGrD{W6sg+0rMEZwFxgyOMX_ZKuMcOOU*G5Vx>pF=8BYh<$xw5(_ zRp)10)x%XCFC8H1lGnvZj=BO8*(m~5*SL86qg z)juIoTh>->Gwe2Beb`EW>Bjnm zESiM) z)H{%Lj{VF?wz|`#RyXylmMTJtBQ1(lL?L2v;v|q}ozG(p9Eh ztuNB63q!`{Ho`6wX`qoDH4+llD(UJjxjI#@7D8HyBVD~JYzHJAj*lSe9C1|GuaNZE zWsLLy{3Bfx>5#OE`XY@qdi_+DmzC-SZmT6yet+NS_Tgf}vP2pt(oB*18rQB;Lm-X9 zah19c-#IK5h_phamqpqx(gz}aD$*}TvQ-=Sl;R+v&Va>lBtJjBG{iN{AeDi08hxVg z4N0eOSCK9k>1vTii8R?r=-Wcly`WN&UNTa;+G_M{^$Db3ao8$zly=uqEgYet3;T|+uSHU$-K%vVX|wG_>MK{X zh2@B}RHWxb+Ah*Tk$w>=d6Vl#Q;~X#G(w~aB270^LM?-&!@FLje~a{iNXJAvB~r@G zuGwlLH4y0{k@|=<)JTrH9TLTht#Tmgx;Xcrx?k+5hvn)DNIKn~g+zUeL2473K)$lo zJCLX!Hb{LZ%y*012Z@VR4U#s|QlwrYjTC93ksOr=NvGRlBiU*VB<+`f8EKH(35jfW z)aOReQ70kk5mk7MD^(TgjR%6}YIDPEbulE}PwWFp`*gTSe$b6rOa5NW7LqeS|LNcR~DqdQ3C8s-!r>D=@< zB;AU?C~U8=FGc#zNVW=(#kc^6t(xLn_aa*h>nc)Dk@|_=IAIeZ>5xAxS4)IF4T

_3G`1@3X(`KXFW|kl#MRTMzP&^ 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 z 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