using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Linq.Expressions; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Media; using InABox.Core; using InABox.Wpf; using Image = System.Windows.Controls.Image; using System.Runtime.InteropServices; using System.Windows.Interop; namespace InABox.WPF; public class FuncTemplateSelector : DataTemplateSelector { public Func TemplateFunc { get; set; } public FuncTemplateSelector(Func templateFunc) { TemplateFunc = templateFunc; } public override DataTemplate SelectTemplate(object item, DependencyObject container) { return TemplateGenerator.CreateDataTemplate(() => { return TemplateFunc(item, container); }); } } public static class WPFUtils { public static void MoveToCenter(this Window window) { if (!GetCursorPos(out POINT cursorPoint)) return; IntPtr mainWindow = MonitorFromPoint(new POINT() { x = 1, y = 1 }, MONITOR_DEFAULTTO.MONITOR_DEFAULTTONULL); IntPtr monitorHandle = MonitorFromPoint(cursorPoint, MONITOR_DEFAULTTO.MONITOR_DEFAULTTONULL); MONITORINFO monitorInfo = new() { cbSize = (uint)Marshal.SizeOf() }; if (!GetMonitorInfo(monitorHandle, ref monitorInfo)) return; IntPtr windowHandle = new WindowInteropHelper(window).EnsureHandle(); if (!GetWindowPlacement(windowHandle, out WINDOWPLACEMENT windowPlacement)) return; int left = monitorInfo.rcWork.left + Math.Max(0, (int)((monitorInfo.rcWork.Width - windowPlacement.rcNormalPosition.Width) / 2D)); int top = monitorInfo.rcWork.top + Math.Max(0, (int)((monitorInfo.rcWork.Height - windowPlacement.rcNormalPosition.Height) / 2D)); windowPlacement.rcNormalPosition = new RECT(left, top, windowPlacement.rcNormalPosition.Width, windowPlacement.rcNormalPosition.Height); SetWindowPlacement(windowHandle, ref windowPlacement); } [DllImport("User32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool GetCursorPos(out POINT lpPoint); [DllImport("User32.dll")] private static extern IntPtr MonitorFromPoint(POINT pt, MONITOR_DEFAULTTO dwFlags); private enum MONITOR_DEFAULTTO : uint { MONITOR_DEFAULTTONULL = 0x00000000, MONITOR_DEFAULTTOPRIMARY = 0x00000001, MONITOR_DEFAULTTONEAREST = 0x00000002, } [DllImport("User32.dll")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFO lpmi); [StructLayout(LayoutKind.Sequential)] private struct MONITORINFO { public uint cbSize; public RECT rcMonitor; public RECT rcWork; public uint dwFlags; } [DllImport("User32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool GetWindowPlacement(IntPtr hWnd, out WINDOWPLACEMENT lpwndpl); [DllImport("User32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool SetWindowPlacement(IntPtr hWnd, [In] ref WINDOWPLACEMENT lpwndpl); [StructLayout(LayoutKind.Sequential)] private struct WINDOWPLACEMENT { public uint length; public uint flags; public uint showCmd; public POINT ptMinPosition; public POINT ptMaxPosition; public RECT rcNormalPosition; } [StructLayout(LayoutKind.Sequential)] private struct POINT { public int x; public int y; } [StructLayout(LayoutKind.Sequential)] private struct RECT { public int left; public int top; public int right; public int bottom; public int Width => right - left; public int Height => bottom - top; public RECT(int x, int y, int width, int height) { left = x; top = y; right = x + width; bottom = y + height; } } #region Setters/Triggers etc public static Style AddSetter(this Style style, DependencyProperty property, object value) { style.Setters.Add(new Setter(property, value)); return style; } public static DataTrigger AddSetter(this DataTrigger trigger, DependencyProperty property, object value) { trigger.Setters.Add(new Setter(property, value)); return trigger; } public static DataTrigger AddDataTrigger(this Style style) { var trigger = new DataTrigger(); style.Triggers.Add(trigger); return trigger; } public static DataTrigger AddDataTrigger(this Style style, Binding binding, object value) { var trigger = new DataTrigger() { Binding = binding, Value = value }; style.Triggers.Add(trigger); return trigger; } #endregion public static SolidColorBrush ToBrush(this System.Windows.Media.Color color, double opacity = 1.0) { return new SolidColorBrush(color) { Opacity = opacity }; } #region Multi-Binding public static MultiBinding CreateMultiBinding(IMultiValueConverter? converter = null, string? format = null, object? parameter = null) { return new MultiBinding { Converter = converter, ConverterParameter = parameter, StringFormat = format }; } public static MultiBinding AddBinding(this MultiBinding multi, BindingBase binding) { multi.Bindings.Add(binding); return multi; } #endregion #region Binding public static Binding CreateBinding( T source, Expression> expression, IValueConverter? converter = null, string? format = null) { return new Binding(CoreUtils.GetFullPropertyName(expression, "_")) { Source = source, Converter = converter, StringFormat = format }; } public static Binding CreateBinding( Expression> expression, IValueConverter? converter = null, BindingMode mode = BindingMode.Default, string? format = null, RelativeSource? relativeSource = null) { return new Binding(CoreUtils.GetFullPropertyName(expression, "_")) { Converter = converter, StringFormat = format, Mode = mode, RelativeSource = relativeSource }; } public static DataTrigger Bind( this DataTrigger trigger, T source, Expression> expression, TValue value, IValueConverter? converter, string? format = null) { trigger.Binding = CreateBinding(source, expression, converter, format); trigger.Value = value; return trigger; } public static DataTrigger Bind( this DataTrigger trigger, Expression> expression, TValue value, IValueConverter? converter, BindingMode mode = BindingMode.Default, string? format = null, RelativeSource? relativeSource = null) { trigger.Binding = CreateBinding(expression, converter, mode, format, relativeSource: relativeSource); trigger.Value = value; return trigger; } public static DataTrigger Bind( this DataTrigger trigger, T source, Expression> expression, TProperty value, IValueConverter? converter = null, string? format = null) { trigger.Binding = CreateBinding(source, expression, converter, format); trigger.Value = value; return trigger; } public static DataTrigger Bind( this DataTrigger trigger, Expression> expression, TProperty value, IValueConverter? converter = null, BindingMode mode = BindingMode.Default, string? format = null, RelativeSource? relativeSource = null) { trigger.Binding = CreateBinding(expression, converter, mode, format, relativeSource: relativeSource); trigger.Value = value; return trigger; } public static void Bind( this FrameworkElement element, DependencyProperty property, T source, Expression> expression, IValueConverter? converter = null, string? format = null) { element.SetBinding( property, CreateBinding(source, expression, converter, format) ); } public static void Bind( this FrameworkElement element, DependencyProperty property, Expression> expression, IValueConverter? converter = null, BindingMode mode = BindingMode.Default, string? format = null, RelativeSource? relativeSource = null) { element.SetBinding( property, CreateBinding(expression, converter, mode, format, relativeSource: relativeSource) ); } public static FrameworkElementFactory Bind( this FrameworkElementFactory element, DependencyProperty property, T source, Expression> expression, IValueConverter? converter = null, string? format = null) { element.SetBinding( property, CreateBinding(source, expression, converter, format) ); return element; } public static FrameworkElementFactory Bind( this FrameworkElementFactory element, DependencyProperty property, Expression> expression, IValueConverter? converter = null, BindingMode mode = BindingMode.Default, string? format = null, RelativeSource? relativeSource = null) { element.SetBinding( property, CreateBinding(expression, converter, mode, format, relativeSource: relativeSource ?? RelativeSource.TemplatedParent) ); return element; } #endregion #region Converters public static IMultiValueConverter WrapConverter(this IValueConverter converter, Func convert, Func? convertBack = null) { return new MultiFuncConverter(convert, convertBack, converter); } #endregion public static T? FindLogicalParent(this DependencyObject dependencyObject) where T : DependencyObject { DependencyObject? parent = dependencyObject; do { parent = LogicalTreeHelper.GetParent(parent); } while(parent != null && parent is not T); return parent as T; } public static IEnumerable FindVisualChildren(this DependencyObject depObj, bool recursive = true) { if (depObj != null) for (var i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++) { var child = VisualTreeHelper.GetChild(depObj, i); if (child is null) continue; if (child is T t) yield return t; if (recursive) { foreach (var childOfChild in FindVisualChildren(child)) yield return childOfChild; } } } public static T? FindChild(this DependencyObject parent, string childName) where T : FrameworkElement { return parent.FindVisualChildren().FirstOrDefault(x => x.Name == childName); } #region Grid Children public static int GetRow(this Grid grid, DependencyObject dependencyObject) { while (true) { var parent = LogicalTreeHelper.GetParent(dependencyObject); if (parent == null) return -1; if (parent == grid) return Grid.GetRow(dependencyObject as UIElement); dependencyObject = parent; } } public static int GetRowSpan(this Grid grid, DependencyObject dependencyObject) { while (true) { var parent = LogicalTreeHelper.GetParent(dependencyObject); if (parent == null) return -1; if (parent == grid) return Grid.GetRowSpan(dependencyObject as UIElement); dependencyObject = parent; } } public static int GetColumn(this Grid grid, DependencyObject dependencyObject) { while (true) { var parent = LogicalTreeHelper.GetParent(dependencyObject); if (parent == null) return -1; if (parent == grid) return Grid.GetColumn(dependencyObject as UIElement); dependencyObject = parent; } } public static int GetColumnSpan(this Grid grid, DependencyObject dependencyObject) { while (true) { var parent = LogicalTreeHelper.GetParent(dependencyObject); if (parent == null) return -1; if (parent == grid) return Grid.GetColumnSpan(dependencyObject as UIElement); dependencyObject = parent; } } public static void SetGridPosition(this FrameworkElement element, int row, int column, int rowspan = 1, int colspan = 1) { element.SetValue(Grid.ColumnProperty, column); element.SetValue(Grid.ColumnSpanProperty, Math.Max(1, colspan)); element.SetValue(Grid.RowProperty, row); element.SetValue(Grid.RowSpanProperty, Math.Max(1, rowspan)); } public static Grid AddChild(this Grid grid, FrameworkElement element, int row, int column, int rowSpan = 1, int colSpan = 1) { element.SetGridPosition(row, column, rowSpan, colSpan); grid.Children.Add(element); return grid; } #endregion #region Grid Columns + Rows public static ColumnDefinition AddColumn(this Grid grid, GridUnitType type, double value = 1) { var colDef = new ColumnDefinition { Width = new GridLength(value, type) }; grid.ColumnDefinitions.Add(colDef); return colDef; } public static ColumnDefinition AddColumn(this Grid grid, double value) { var colDef = new ColumnDefinition { Width = new GridLength(value) }; grid.ColumnDefinitions.Add(colDef); return colDef; } public static RowDefinition AddRow(this Grid grid, GridUnitType type, double value = 1) { var rowDef = new RowDefinition { Height = new GridLength(value, type) }; grid.RowDefinitions.Add(rowDef); return rowDef; } public static RowDefinition AddRow(this Grid grid, double value) { var rowDef = new RowDefinition { Height = new GridLength(value) }; grid.RowDefinitions.Add(rowDef); return rowDef; } #endregion #region Menu Utils private static void ItemsControlInsert(ItemsControl menu, FrameworkElement item, int index) { if (index != -1) { menu.Items.Insert(index, item); } else { menu.Items.Add(item); } } private static MenuItem DoAddMenuItem(ItemsControl menu, string caption, Bitmap? image, bool enabled, int index = -1) { var item = new MenuItem { Header = caption, IsEnabled = enabled }; if (image != null) item.Icon = new Image() { Source = enabled ? image.AsBitmapImage(24, 24) : image.AsGrayScale().AsBitmapImage(24, 24) }; ItemsControlInsert(menu, item, index); return item; } private static MenuItem DoAddMenuItem(ItemsControl menu, string caption, Bitmap? image, Action? click, bool enabled, int index = -1) { var item = DoAddMenuItem(menu, caption, image, enabled, index); if (click != null) { item.Click += (o, e) => { click(); }; } return item; } private static MenuItem DoAddMenuItem(ItemsControl menu, string caption, Bitmap? image, T tag, Action click, bool enabled, int index = -1) { var item = DoAddMenuItem(menu, caption, image, enabled, index); item.Tag = tag; item.Click += (o, e) => { click((T)(o as MenuItem)!.Tag); }; return item; } public delegate void CheckToggleAction(bool isChecked); public delegate void CheckToggleAction(T tag, bool isChecked); private static MenuItem DoAddCheckItem(ItemsControl menu, string caption, CheckToggleAction click, bool isChecked, bool enabled, int index = -1) { var item = new MenuItem { Header = caption, IsEnabled = enabled, IsCheckable = true, IsChecked = isChecked }; item.Click += (o, e) => { click(item.IsChecked); }; ItemsControlInsert(menu, item, index); return item; } private static MenuItem DoAddCheckItem(ItemsControl menu, string caption, T tag, CheckToggleAction click, bool isChecked, bool enabled, int index = -1) { var item = new MenuItem { Header = caption, IsEnabled = enabled, IsCheckable = true, IsChecked = isChecked }; item.Tag = tag; item.Click += (o, e) => { click((T)(o as MenuItem)!.Tag, item.IsChecked); }; ItemsControlInsert(menu, item, index); return item; } private static Separator DoAddSeparator(ItemsControl menu, int index) { var separator = new Separator(); ItemsControlInsert(menu, separator, index); return separator; } private static Separator? DoAddSeparatorIfNeeded(ItemsControl menu, int index) { if (menu.Items.Count == 0) return null; var lastIndex = index != -1 ? index - 1 : menu.Items.Count - 1; if (lastIndex < 0 || lastIndex >= menu.Items.Count) return null; var last = menu.Items[lastIndex]; if (last is Separator) return null; var separator = new Separator(); ItemsControlInsert(menu, separator, index); return separator; } private static void DoRemoveUnnecessarySeparators(ItemsControl menu) { while(menu.Items.Count > 0 && menu.Items[0] is Separator) { menu.Items.RemoveAt(0); } while(menu.Items.Count > 0 && menu.Items[^1] is Separator) { menu.Items.RemoveAt(menu.Items.Count - 1); } } public static Separator AddSeparator(this ContextMenu menu, int index = -1) => DoAddSeparator(menu, index); public static Separator AddSeparator(this MenuItem menu, int index = -1) => DoAddSeparator(menu, index); public static Separator? AddSeparatorIfNeeded(this ContextMenu menu, int index = -1) => DoAddSeparatorIfNeeded(menu, index); public static Separator? AddSeparatorIfNeeded(this MenuItem menu, int index = -1) => DoAddSeparatorIfNeeded(menu, index); public static void RemoveUnnecessarySeparators(this ContextMenu menu) => DoRemoveUnnecessarySeparators(menu); public static void RemoveUnnecessarySeparators(this MenuItem menu) => DoRemoveUnnecessarySeparators(menu); public static MenuItem AddItem(this ContextMenu menu, string caption, Bitmap? image, Action? click, bool enabled = true, int index = -1) => DoAddMenuItem(menu, caption, image, click, enabled, index); public static MenuItem AddItem(this MenuItem menu, string caption, Bitmap? image, Action? click, bool enabled = true, int index = -1) => DoAddMenuItem(menu, caption, image, click, enabled, index); public static MenuItem AddItem(this ContextMenu menu, string caption, Bitmap? image, T tag, Action click, bool enabled = true, int index = -1) => DoAddMenuItem(menu, caption, image, tag, click, enabled, index); public static MenuItem AddItem(this MenuItem menu, string caption, Bitmap? image, T tag, Action click, bool enabled = true, int index = -1) => DoAddMenuItem(menu, caption, image, tag, click, enabled, index); public static MenuItem AddCheckItem(this ContextMenu menu, string caption, CheckToggleAction click, bool isChecked = false, bool enabled = true, int index = -1) => DoAddCheckItem(menu, caption, click, isChecked, enabled, index); public static MenuItem AddCheckItem(this ContextMenu menu, string caption, T tag, CheckToggleAction click, bool isChecked = false, bool enabled = true, int index = -1) => DoAddCheckItem(menu, caption, tag, click, isChecked, enabled, index); #endregion }