Squashed 'FSI.Lib/' content from commit 6aa4846
git-subtree-dir: FSI.Lib git-subtree-split: 6aa48465a834a7bfdd9cbeae8d2e4f769d0c0ff8
This commit is contained in:
27
FSI.Lib/Wpf/Converter/BooleanConverter.cs
Normal file
27
FSI.Lib/Wpf/Converter/BooleanConverter.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace FSI.Lib.Wpf.Converter
|
||||
{
|
||||
public class BooleanConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
return !((bool)value);
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
public object ProvideValue(IServiceProvider serviceProvider)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
22
FSI.Lib/Wpf/Converters/InvertedBoolenConverter .cs
Normal file
22
FSI.Lib/Wpf/Converters/InvertedBoolenConverter .cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace FSI.Lib.Wpf.Converters
|
||||
{
|
||||
|
||||
public class InvertedBoolenConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
return !(bool)value;
|
||||
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
return (bool)value;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
16
FSI.Lib/Wpf/Ctrls/ChbWindowsTopMost.xaml
Normal file
16
FSI.Lib/Wpf/Ctrls/ChbWindowsTopMost.xaml
Normal file
@@ -0,0 +1,16 @@
|
||||
<UserControl x:Class="FSI.Lib.Wpf.Ctrls.ChbWindowsTopMost"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:FSI.Lib.Wpf.Ctrls"
|
||||
mc:Ignorable="d"
|
||||
Width="Auto"
|
||||
Height="Auto">
|
||||
<StackPanel>
|
||||
<CheckBox Name="chbTopMost"
|
||||
Content="Fenster immer im Vordergrund"
|
||||
Checked="CheckBox_Checked"
|
||||
Unchecked="CheckBox_Unchecked" />
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
89
FSI.Lib/Wpf/Ctrls/ChbWindowsTopMost.xaml.cs
Normal file
89
FSI.Lib/Wpf/Ctrls/ChbWindowsTopMost.xaml.cs
Normal file
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Interop;
|
||||
|
||||
namespace FSI.Lib.Wpf.Ctrls
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaktionslogik für ChbWindowsTopMost.xaml
|
||||
/// </summary>
|
||||
public partial class ChbWindowsTopMost : UserControl
|
||||
{
|
||||
private Window _window;
|
||||
|
||||
//A window receives this message when the user chooses a command from the Window menu, or when the user chooses the maximize button, minimize button, restore button, or close button.
|
||||
public const Int32 WM_SYSCOMMAND = 0x112;
|
||||
|
||||
//Draws a horizontal dividing line.This flag is used only in a drop-down menu, submenu, or shortcut menu.The line cannot be grayed, disabled, or highlighted.
|
||||
public const Int32 MF_SEPARATOR = 0x800;
|
||||
|
||||
//Specifies that an ID is a position index into the menu and not a command ID.
|
||||
public const Int32 MF_BYPOSITION = 0x400;
|
||||
|
||||
//Specifies that the menu item is a text string.
|
||||
public const Int32 MF_STRING = 0x0;
|
||||
|
||||
//Menu Ids for our custom menu items
|
||||
public const Int32 _ItemTopMostId = 1000;
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern bool InsertMenu(IntPtr hMenu, Int32 wPosition, Int32 wFlags, Int32 wIDNewItem, string lpNewItem);
|
||||
|
||||
public ChbWindowsTopMost()
|
||||
{
|
||||
InitializeComponent();
|
||||
Loaded += ChbWindowsTopMost_Loaded;
|
||||
}
|
||||
|
||||
private void ChbWindowsTopMost_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_window = Window.GetWindow(this);
|
||||
|
||||
IntPtr windowhandle = new WindowInteropHelper(_window).Handle;
|
||||
HwndSource hwndSource = HwndSource.FromHwnd(windowhandle);
|
||||
|
||||
//Get the handle for the system menu
|
||||
IntPtr systemMenuHandle = GetSystemMenu(windowhandle, false);
|
||||
|
||||
//Insert our custom menu items
|
||||
InsertMenu(systemMenuHandle, 5, MF_BYPOSITION | MF_SEPARATOR, 0, string.Empty); //Add a menu seperator
|
||||
InsertMenu(systemMenuHandle, 6, MF_BYPOSITION, _ItemTopMostId, "immer im Vordergrund"); //Add a setting menu item
|
||||
|
||||
hwndSource.AddHook(new HwndSourceHook(WndProc));
|
||||
}
|
||||
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
|
||||
{
|
||||
// Check if the SystemCommand message has been executed
|
||||
if (msg == WM_SYSCOMMAND)
|
||||
{
|
||||
//check which menu item was clicked
|
||||
switch (wParam.ToInt32())
|
||||
{
|
||||
case _ItemTopMostId:
|
||||
_window.Topmost = !_window.Topmost;
|
||||
chbTopMost.IsChecked = _window.Topmost;
|
||||
handled = true;
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
private void CheckBox_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_window.Topmost = true;
|
||||
}
|
||||
|
||||
private void CheckBox_Unchecked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_window.Topmost = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
78
FSI.Lib/Wpf/Ctrls/FilterDataGrid/DataGridColumn.cs
Normal file
78
FSI.Lib/Wpf/Ctrls/FilterDataGrid/DataGridColumn.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
using System.Windows;
|
||||
|
||||
namespace FSI.Lib.Wpf.Ctrls.FilterDataGrid
|
||||
{
|
||||
public sealed class DataGridTemplateColumn : System.Windows.Controls.DataGridTemplateColumn
|
||||
{
|
||||
#region Public Fields
|
||||
|
||||
/// <summary>
|
||||
/// FieldName Dependency Property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty FieldNameProperty =
|
||||
DependencyProperty.Register("FieldName", typeof(string), typeof(DataGridTemplateColumn),
|
||||
new PropertyMetadata(""));
|
||||
|
||||
/// <summary>
|
||||
/// IsColumnFiltered Dependency Property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty IsColumnFilteredProperty =
|
||||
DependencyProperty.Register("IsColumnFiltered", typeof(bool), typeof(DataGridTemplateColumn),
|
||||
new PropertyMetadata(false));
|
||||
|
||||
#endregion Public Fields
|
||||
|
||||
#region Public Properties
|
||||
|
||||
public string FieldName
|
||||
{
|
||||
get => (string)GetValue(FieldNameProperty);
|
||||
set => SetValue(FieldNameProperty, value);
|
||||
}
|
||||
|
||||
public bool IsColumnFiltered
|
||||
{
|
||||
get => (bool)GetValue(IsColumnFilteredProperty);
|
||||
set => SetValue(IsColumnFilteredProperty, value);
|
||||
}
|
||||
|
||||
#endregion Public Properties
|
||||
}
|
||||
|
||||
public sealed class DataGridTextColumn : System.Windows.Controls.DataGridTextColumn
|
||||
{
|
||||
#region Public Fields
|
||||
|
||||
/// <summary>
|
||||
/// FieldName Dependency Property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty FieldNameProperty =
|
||||
DependencyProperty.Register("FieldName", typeof(string), typeof(DataGridTextColumn),
|
||||
new PropertyMetadata(""));
|
||||
|
||||
/// <summary>
|
||||
/// IsColumnFiltered Dependency Property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty IsColumnFilteredProperty =
|
||||
DependencyProperty.Register("IsColumnFiltered", typeof(bool), typeof(DataGridTextColumn),
|
||||
new PropertyMetadata(false));
|
||||
|
||||
#endregion Public Fields
|
||||
|
||||
#region Public Properties
|
||||
|
||||
public string FieldName
|
||||
{
|
||||
get => (string)GetValue(FieldNameProperty);
|
||||
set => SetValue(FieldNameProperty, value);
|
||||
}
|
||||
|
||||
public bool IsColumnFiltered
|
||||
{
|
||||
get => (bool)GetValue(IsColumnFilteredProperty);
|
||||
set => SetValue(IsColumnFilteredProperty, value);
|
||||
}
|
||||
|
||||
#endregion Public Properties
|
||||
}
|
||||
}
|
||||
319
FSI.Lib/Wpf/Ctrls/FilterDataGrid/FilterCommon.cs
Normal file
319
FSI.Lib/Wpf/Ctrls/FilterDataGrid/FilterCommon.cs
Normal file
@@ -0,0 +1,319 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FSI.Lib.Wpf.Ctrls.FilterDataGrid
|
||||
{
|
||||
public sealed class FilterCommon : NotifyProperty
|
||||
{
|
||||
#region Public Constructors
|
||||
|
||||
public FilterCommon()
|
||||
{
|
||||
PreviouslyFilteredItems = new HashSet<object>(EqualityComparer<object>.Default);
|
||||
}
|
||||
|
||||
#endregion Public Constructors
|
||||
|
||||
#region Public Properties
|
||||
|
||||
public string FieldName { get; set; }
|
||||
public Type FieldType { get; set; }
|
||||
public bool IsFiltered { get; set; }
|
||||
public HashSet<object> PreviouslyFilteredItems { get; set; }
|
||||
|
||||
// Treeview
|
||||
public List<FilterItem> Tree { get; set; }
|
||||
|
||||
public Loc Translate { get; set; }
|
||||
|
||||
#endregion Public Properties
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Recursive call for check/uncheck all items in tree
|
||||
/// </summary>
|
||||
/// <param name="state"></param>
|
||||
/// <param name="updateChildren"></param>
|
||||
/// <param name="updateParent"></param>
|
||||
private void SetIsChecked(FilterItem item, bool? state, bool updateChildren, bool updateParent)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (state == item.IsChecked) return;
|
||||
item.SetState = state;
|
||||
|
||||
// select all / unselect all
|
||||
if (item.Level == 0)
|
||||
Tree.Where(t => t.Level != 0).ToList().ForEach(c => { SetIsChecked(c, state, true, true); });
|
||||
|
||||
// update children
|
||||
if (updateChildren && item.IsChecked.HasValue)
|
||||
item.Children?.ForEach(c => { SetIsChecked(c, state, true, false); });
|
||||
|
||||
// update parent
|
||||
if (updateParent && item.Parent != null)
|
||||
VerifyCheckedState(item.Parent);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"FilterCommon.SetState : {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the tree when the state of the IsChecked property changes
|
||||
/// </summary>
|
||||
/// <param name="o">item</param>
|
||||
/// <param name="e">state</param>
|
||||
public void UpdateTree(object o, bool? e)
|
||||
{
|
||||
if (o == null) return;
|
||||
var item = (FilterItem)o;
|
||||
SetIsChecked(item, e, true, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check or uncheck parents or children
|
||||
/// </summary>
|
||||
private void VerifyCheckedState(FilterItem item)
|
||||
{
|
||||
bool? state = null;
|
||||
|
||||
for (var i = 0; i < item.Children?.Count; ++i)
|
||||
{
|
||||
var current = item.Children[i].IsChecked;
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
state = current;
|
||||
}
|
||||
else if (state != current)
|
||||
{
|
||||
state = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SetIsChecked(item, state, false, true);
|
||||
}
|
||||
|
||||
#endregion Private Methods
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Add the filter to the predicate dictionary
|
||||
/// </summary>
|
||||
public void AddFilter(Dictionary<string, Predicate<object>> criteria)
|
||||
{
|
||||
if (IsFiltered) return;
|
||||
|
||||
// predicate of filter
|
||||
bool Predicate(object o)
|
||||
{
|
||||
var value = o.GetType().GetProperty(FieldName)?.GetValue(o, null);
|
||||
return !PreviouslyFilteredItems.Contains(value);
|
||||
}
|
||||
|
||||
// add to list of predicates
|
||||
criteria.Add(FieldName, Predicate);
|
||||
|
||||
IsFiltered = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Any Date IsChecked, check if any tree item is checked (can apply filter)
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool AnyDateIsChecked()
|
||||
{
|
||||
// any IsChecked is true or null
|
||||
// IsDate Checked has three states, isChecked: null and true
|
||||
return Tree != null && Tree.Skip(1).Any(t => t.IsChecked != false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Any state of Date Changed, check if at least one date is checked and another is changed
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool AnyDateChanged()
|
||||
{
|
||||
// any (year, month, day) status changed
|
||||
return Tree != null &&
|
||||
Tree.Skip(1)
|
||||
.Any(year => year.Changed || year.Children
|
||||
.Any(month => month.Changed || month.Children
|
||||
.Any(day => day.Changed))) && AnyDateIsChecked();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Build the item tree
|
||||
/// </summary>
|
||||
/// <param name="dates"></param>
|
||||
/// <param name="currentFilter"></param>
|
||||
/// <param name="uncheckPrevious"></param>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<FilterItem> BuildTree(IEnumerable<object> dates, string lastFilter = null)
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
var uncheckPrevious = FieldName == lastFilter;
|
||||
var type = typeof(DateTime);
|
||||
|
||||
Tree = new List<FilterItem>
|
||||
{
|
||||
new FilterItem(this)
|
||||
{
|
||||
Label = Translate.All, CurrentFilter = this, Content = 0, Level = 0, SetState = true, FieldType = type
|
||||
}
|
||||
};
|
||||
|
||||
if (dates == null) return Tree;
|
||||
// iterate over all items that are not null
|
||||
// INFO:
|
||||
// SetState : does not raise OnDateStatusChanged event
|
||||
// IsChecked : raise OnDateStatusChanged event
|
||||
// (see the FilterItem class for more informations)
|
||||
|
||||
var dateTimes = dates.ToList();
|
||||
|
||||
foreach (var y in from date in dateTimes.Where(d => d != null)
|
||||
.Select(d => (DateTime)d).OrderBy(o => o.Year)
|
||||
group date by date.Year into year
|
||||
select new FilterItem(this)
|
||||
{
|
||||
// YEAR
|
||||
Level = 1,
|
||||
CurrentFilter = this,
|
||||
Content = year.Key,
|
||||
Label = year.First().ToString("yyyy", Translate.Culture),
|
||||
SetState = true, // default state
|
||||
FieldType = type,
|
||||
|
||||
Children = (from date in year
|
||||
group date by date.Month into month
|
||||
select new FilterItem(this)
|
||||
{
|
||||
// MOUNTH
|
||||
Level = 2,
|
||||
CurrentFilter = this,
|
||||
Content = month.Key,
|
||||
Label = month.First().ToString("MMMM", Translate.Culture),
|
||||
SetState = true, // default state
|
||||
FieldType = type,
|
||||
|
||||
Children = (from day in month
|
||||
select new FilterItem(this)
|
||||
{
|
||||
// DAY
|
||||
Level = 3,
|
||||
CurrentFilter = this,
|
||||
Content = day.Day,
|
||||
Label = day.ToString("dd", Translate.Culture),
|
||||
SetState = true, // default state
|
||||
FieldType = type,
|
||||
Children = new List<FilterItem>()
|
||||
}).ToList()
|
||||
}).ToList()
|
||||
})
|
||||
{
|
||||
// set parent and IsChecked property if uncheckPrevious items
|
||||
y.Children.ForEach(m =>
|
||||
{
|
||||
m.Parent = y;
|
||||
|
||||
m.Children.ForEach(d =>
|
||||
{
|
||||
d.Parent = m;
|
||||
|
||||
// set the state of the ischecked property based on the items already filtered (unchecked)
|
||||
if (PreviouslyFilteredItems != null && uncheckPrevious)
|
||||
d.IsChecked = PreviouslyFilteredItems
|
||||
.Any(u => u != null && u.Equals(new DateTime((int)y.Content, (int)m.Content, (int)d.Content))) == false;
|
||||
|
||||
// reset initialization with new state
|
||||
d.InitialState = d.IsChecked;
|
||||
});
|
||||
|
||||
// reset initialization with new state
|
||||
m.InitialState = m.IsChecked;
|
||||
});
|
||||
|
||||
// reset initialization with new state
|
||||
y.InitialState = y.IsChecked;
|
||||
|
||||
Tree.Add(y);
|
||||
}
|
||||
|
||||
// last empty item if exist in collection
|
||||
if (dateTimes.Any(d => d == null))
|
||||
Tree.Add(
|
||||
new FilterItem(this)
|
||||
{
|
||||
Label = Translate.Empty, // translation
|
||||
CurrentFilter = this,
|
||||
Content = null,
|
||||
Level = -1,
|
||||
FieldType = type,
|
||||
SetState = PreviouslyFilteredItems?.Any(u => u == null) == false,
|
||||
Children = new List<FilterItem>()
|
||||
}
|
||||
);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"FilterCommon.BuildTree : {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
|
||||
return Tree;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all the items from the tree (checked or unchecked)
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<FilterItem> GetAllItemsTree()
|
||||
{
|
||||
var filterCommon = new List<FilterItem>();
|
||||
|
||||
try
|
||||
{
|
||||
// skip first item (select all)
|
||||
foreach (var y in Tree.Skip(1))
|
||||
if (y.Level > 0) // year :1, mounth : 2, day : 3
|
||||
filterCommon.AddRange(
|
||||
from m in y.Children
|
||||
from d in m.Children
|
||||
select new FilterItem
|
||||
{
|
||||
Content = new DateTime((int)y.Content, (int)m.Content, (int)d.Content),
|
||||
IsChecked = d.IsChecked ?? false,
|
||||
});
|
||||
else // null date (Level -1)
|
||||
filterCommon.Add(new FilterItem
|
||||
{
|
||||
Content = null,
|
||||
IsChecked = y.IsChecked ?? false,
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"FilterCommon.GetAllItemsTree : {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
|
||||
return filterCommon;
|
||||
}
|
||||
|
||||
#endregion Public Methods
|
||||
}
|
||||
}
|
||||
|
||||
1309
FSI.Lib/Wpf/Ctrls/FilterDataGrid/FilterDataGrid.cs
Normal file
1309
FSI.Lib/Wpf/Ctrls/FilterDataGrid/FilterDataGrid.cs
Normal file
File diff suppressed because it is too large
Load Diff
20
FSI.Lib/Wpf/Ctrls/FilterDataGrid/FilterDataGridDictionary.cs
Normal file
20
FSI.Lib/Wpf/Ctrls/FilterDataGrid/FilterDataGridDictionary.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
namespace FSI.Lib.Wpf.Ctrls.FilterDataGrid
|
||||
{
|
||||
/// <summary>
|
||||
/// ResourceDictionary
|
||||
/// </summary>
|
||||
public partial class FilterDataGridDictionary
|
||||
{
|
||||
#region Public Constructors
|
||||
|
||||
/// <summary>
|
||||
/// FilterDataGrid Dictionary
|
||||
/// </summary>
|
||||
public FilterDataGridDictionary()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
#endregion Public Constructors
|
||||
}
|
||||
}
|
||||
396
FSI.Lib/Wpf/Ctrls/FilterDataGrid/FilterHelpers.cs
Normal file
396
FSI.Lib/Wpf/Ctrls/FilterDataGrid/FilterHelpers.cs
Normal file
@@ -0,0 +1,396 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Controls.Primitives;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace FSI.Lib.Wpf.Ctrls.FilterDataGrid
|
||||
{
|
||||
public static class Extensions
|
||||
{
|
||||
#region Public Methods
|
||||
|
||||
public static HashSet<T> ToHashSet<T>(this IEnumerable<T> source, IEqualityComparer<T> comparer = null)
|
||||
{
|
||||
return new HashSet<T>(source, comparer);
|
||||
}
|
||||
|
||||
#endregion Public Methods
|
||||
}
|
||||
|
||||
public static class Helpers
|
||||
{
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Print elapsed time
|
||||
/// </summary>
|
||||
/// <param name="label"></param>
|
||||
/// <param name="start"></param>
|
||||
public static void Elapsed(string label, DateTime start)
|
||||
{
|
||||
var span = DateTime.Now - start;
|
||||
Debug.WriteLine($"{label,-20}{span:mm\\:ss\\.ff}");
|
||||
}
|
||||
|
||||
#endregion Public Methods
|
||||
}
|
||||
|
||||
public static class VisualTreeHelpers
|
||||
{
|
||||
#region Private Methods
|
||||
|
||||
private static T FindVisualChild<T>(this DependencyObject dependencyObject, string name)
|
||||
where T : DependencyObject
|
||||
{
|
||||
// Search immediate children first (breadth-first)
|
||||
var childrenCount = VisualTreeHelper.GetChildrenCount(dependencyObject);
|
||||
|
||||
//http://stackoverflow.com/questions/12304904/why-visualtreehelper-getchildrencount-returns-0-for-popup
|
||||
|
||||
if (childrenCount == 0 && dependencyObject is Popup)
|
||||
{
|
||||
var popup = dependencyObject as Popup;
|
||||
return popup.Child?.FindVisualChild<T>(name);
|
||||
}
|
||||
|
||||
for (var i = 0; i < childrenCount; i++)
|
||||
{
|
||||
var child = VisualTreeHelper.GetChild(dependencyObject, i);
|
||||
var nameOfChild = child.GetValue(FrameworkElement.NameProperty) as string;
|
||||
|
||||
if (child is T && (name == string.Empty || name == nameOfChild))
|
||||
return (T)child;
|
||||
var childOfChild = child.FindVisualChild<T>(name);
|
||||
if (childOfChild != null)
|
||||
return childOfChild;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static IEnumerable<T> GetChildrenOf<T>(this DependencyObject obj, bool recursive) where T : DependencyObject
|
||||
{
|
||||
var count = VisualTreeHelper.GetChildrenCount(obj);
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var child = VisualTreeHelper.GetChild(obj, i);
|
||||
if (child is T) yield return (T)child;
|
||||
|
||||
if (recursive)
|
||||
foreach (var item in child.GetChildrenOf<T>())
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<T> GetChildrenOf<T>(this DependencyObject obj) where T : DependencyObject
|
||||
{
|
||||
return obj.GetChildrenOf<T>(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method is an alternative to WPF's
|
||||
/// <see cref="VisualTreeHelper.GetParent" /> method, which also
|
||||
/// supports content elements. Keep in mind that for content element,
|
||||
/// this method falls back to the logical tree of the element!
|
||||
/// </summary>
|
||||
/// <param name="child">The item to be processed.</param>
|
||||
/// <returns>
|
||||
/// The submitted item's parent, if available. Otherwise
|
||||
/// null.
|
||||
/// </returns>
|
||||
private static DependencyObject GetParentObject(this DependencyObject child)
|
||||
{
|
||||
if (child == null) return null;
|
||||
|
||||
//handle content elements separately
|
||||
var contentElement = child as ContentElement;
|
||||
if (contentElement != null)
|
||||
{
|
||||
var parent = ContentOperations.GetParent(contentElement);
|
||||
if (parent != null) return parent;
|
||||
|
||||
var fce = contentElement as FrameworkContentElement;
|
||||
return fce?.Parent;
|
||||
}
|
||||
|
||||
//also try searching for parent in framework elements (such as DockPanel, etc)
|
||||
var frameworkElement = child as FrameworkElement;
|
||||
if (frameworkElement != null)
|
||||
{
|
||||
var parent = frameworkElement.Parent;
|
||||
if (parent != null) return parent;
|
||||
}
|
||||
|
||||
//if it's not a ContentElement/FrameworkElement, rely on VisualTreeHelper
|
||||
return VisualTreeHelper.GetParent(child);
|
||||
}
|
||||
#endregion Private Methods
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Returns the first ancester of specified type
|
||||
/// </summary>
|
||||
public static T FindAncestor<T>(DependencyObject current)
|
||||
where T : DependencyObject
|
||||
{
|
||||
current = VisualTreeHelper.GetParent(current);
|
||||
|
||||
while (current != null)
|
||||
{
|
||||
if (current is T) return (T)current;
|
||||
|
||||
current = VisualTreeHelper.GetParent(current);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a specific ancester of an object
|
||||
/// </summary>
|
||||
public static T FindAncestor<T>(DependencyObject current, T lookupItem)
|
||||
where T : DependencyObject
|
||||
{
|
||||
while (current != null)
|
||||
{
|
||||
if (current is T && current == lookupItem) return (T)current;
|
||||
|
||||
current = VisualTreeHelper.GetParent(current);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds an ancestor object by name and type
|
||||
/// </summary>
|
||||
public static T FindAncestor<T>(DependencyObject current, string parentName)
|
||||
where T : DependencyObject
|
||||
{
|
||||
while (current != null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(parentName))
|
||||
{
|
||||
var frameworkElement = current as FrameworkElement;
|
||||
if (current is T && frameworkElement != null && frameworkElement.Name == parentName)
|
||||
return (T)current;
|
||||
}
|
||||
else if (current is T)
|
||||
{
|
||||
return (T)current;
|
||||
}
|
||||
|
||||
current = VisualTreeHelper.GetParent(current);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks for a child control within a parent by name
|
||||
/// </summary>
|
||||
public static T FindChild<T>(DependencyObject parent, string childName) where T : DependencyObject
|
||||
{
|
||||
// Confirm parent and childName are valid.
|
||||
if (parent == null) return null;
|
||||
|
||||
T foundChild = null;
|
||||
|
||||
var childrenCount = VisualTreeHelper.GetChildrenCount(parent);
|
||||
for (var i = 0; i < childrenCount; i++)
|
||||
{
|
||||
var child = VisualTreeHelper.GetChild(parent, i);
|
||||
// If the child is not of the request child type child
|
||||
var childType = child as T;
|
||||
if (childType == null)
|
||||
{
|
||||
// recursively drill down the tree
|
||||
foundChild = FindChild<T>(child, childName);
|
||||
|
||||
// If the child is found, break so we do not overwrite the found child.
|
||||
if (foundChild != null) break;
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(childName))
|
||||
{
|
||||
var frameworkElement = child as FrameworkElement;
|
||||
// If the child's name is set for search
|
||||
if (frameworkElement != null && frameworkElement.Name == childName)
|
||||
{
|
||||
// if the child's name is of the request name
|
||||
foundChild = (T)child;
|
||||
break;
|
||||
}
|
||||
|
||||
// recursively drill down the tree
|
||||
foundChild = FindChild<T>(child, childName);
|
||||
|
||||
// If the child is found, break so we do not overwrite the found child.
|
||||
if (foundChild != null) break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// child element found.
|
||||
foundChild = (T)child;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return foundChild;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks for a child control within a parent by type
|
||||
/// </summary>
|
||||
public static T FindChild<T>(DependencyObject parent)
|
||||
where T : DependencyObject
|
||||
{
|
||||
// Confirm parent is valid.
|
||||
if (parent == null) return null;
|
||||
|
||||
T foundChild = null;
|
||||
|
||||
var childrenCount = VisualTreeHelper.GetChildrenCount(parent);
|
||||
for (var i = 0; i < childrenCount; i++)
|
||||
{
|
||||
var child = VisualTreeHelper.GetChild(parent, i);
|
||||
// If the child is not of the request child type child
|
||||
var childType = child as T;
|
||||
if (childType == null)
|
||||
{
|
||||
// recursively drill down the tree
|
||||
foundChild = FindChild<T>(child);
|
||||
|
||||
// If the child is found, break so we do not overwrite the found child.
|
||||
if (foundChild != null) break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// child element found.
|
||||
foundChild = (T)child;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return foundChild;
|
||||
}
|
||||
|
||||
public static T FindVisualChild<T>(this DependencyObject dependencyObject) where T : DependencyObject
|
||||
{
|
||||
return dependencyObject.FindVisualChild<T>(string.Empty);
|
||||
}
|
||||
|
||||
public static Visual GetDescendantByType(Visual element, Type type)
|
||||
{
|
||||
if (element == null) return null;
|
||||
if (element.GetType() == type) return element;
|
||||
Visual foundElement = null;
|
||||
if (element is FrameworkElement frameworkElement) frameworkElement.ApplyTemplate();
|
||||
for (var i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++)
|
||||
{
|
||||
var visual = VisualTreeHelper.GetChild(element, i) as Visual;
|
||||
foundElement = GetDescendantByType(visual, type);
|
||||
if (foundElement != null) break;
|
||||
}
|
||||
|
||||
return foundElement;
|
||||
}
|
||||
|
||||
public static DataGridColumnHeader GetHeader(DataGridColumn column, DependencyObject reference)
|
||||
{
|
||||
for (var i = 0; i < VisualTreeHelper.GetChildrenCount(reference); i++)
|
||||
{
|
||||
var child = VisualTreeHelper.GetChild(reference, i);
|
||||
|
||||
if (child is DataGridColumnHeader colHeader && colHeader.Column == column) return colHeader;
|
||||
|
||||
colHeader = GetHeader(column, child);
|
||||
if (colHeader != null) return colHeader;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds a parent of a given item on the visual tree.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the queried item.</typeparam>
|
||||
/// <param name="child">
|
||||
/// A direct or indirect child of the
|
||||
/// queried item.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The first parent item that matches the submitted
|
||||
/// type parameter. If not matching item can be found, a null
|
||||
/// reference is being returned.
|
||||
/// </returns>
|
||||
public static T TryFindParent<T>(this DependencyObject child) where T : DependencyObject
|
||||
{
|
||||
//get parent item
|
||||
var parentObject = GetParentObject(child);
|
||||
|
||||
//we've reached the end of the tree
|
||||
if (parentObject == null) return null;
|
||||
|
||||
//check if the parent matches the type we're looking for
|
||||
var parent = parentObject as T;
|
||||
if (parent != null)
|
||||
return parent;
|
||||
return TryFindParent<T>(parentObject);
|
||||
}
|
||||
#endregion Public Methods
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Base class for all ViewModel classes in the application. Provides support for
|
||||
/// property changes notification.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public abstract class NotifyProperty : INotifyPropertyChanged
|
||||
{
|
||||
#region Public Events
|
||||
|
||||
/// <summary>
|
||||
/// Raised when a property on this object has a new value.
|
||||
/// </summary>
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
#endregion Public Events
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Warns the developer if this object does not have a public property with
|
||||
/// the specified name. This method does not exist in a Release build.
|
||||
/// </summary>
|
||||
[Conditional("DEBUG")]
|
||||
[DebuggerStepThrough]
|
||||
private void VerifyPropertyName(string propertyName)
|
||||
{
|
||||
// verify that the property name matches a real,
|
||||
// public, instance property on this object.
|
||||
if (TypeDescriptor.GetProperties(this)[propertyName] == null)
|
||||
Debug.Fail("Invalid property name: " + propertyName);
|
||||
}
|
||||
|
||||
#endregion Private Methods
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Raises this object's PropertyChanged event.
|
||||
/// </summary>
|
||||
/// <param name="propertyName">The name of the property that has a new value.</param>
|
||||
public void OnPropertyChanged(string propertyName)
|
||||
{
|
||||
VerifyPropertyName(propertyName);
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
#endregion Public Methods
|
||||
}
|
||||
}
|
||||
147
FSI.Lib/Wpf/Ctrls/FilterDataGrid/FilterItem.cs
Normal file
147
FSI.Lib/Wpf/Ctrls/FilterDataGrid/FilterItem.cs
Normal file
@@ -0,0 +1,147 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FSI.Lib.Wpf.Ctrls.FilterDataGrid
|
||||
{
|
||||
public class FilterItem : NotifyProperty
|
||||
{
|
||||
#region Public Events
|
||||
|
||||
private event EventHandler<bool?> OnDateStatusChanged;
|
||||
|
||||
#endregion Public Events
|
||||
|
||||
#region Constructor
|
||||
|
||||
public FilterItem(FilterCommon action = null)
|
||||
{
|
||||
// event subscription
|
||||
if (action != null)
|
||||
OnDateStatusChanged += action.UpdateTree;
|
||||
}
|
||||
|
||||
#endregion Constructor
|
||||
|
||||
#region Private Fields
|
||||
|
||||
private bool? isChecked;
|
||||
private bool initialized;
|
||||
|
||||
#endregion Private Fields
|
||||
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
///Children higher levels (years, months)
|
||||
/// </summary>
|
||||
public List<FilterItem> Children { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Raw value of the item (not displayed, see Label property)
|
||||
/// </summary>
|
||||
public object Content { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Content length
|
||||
/// </summary>
|
||||
public int ContentLength { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Current filter
|
||||
/// </summary>
|
||||
public FilterCommon CurrentFilter { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Field type
|
||||
/// </summary>
|
||||
public Type FieldType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initial state
|
||||
/// </summary>
|
||||
public bool? InitialState { get; set; }
|
||||
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// State of checkbox
|
||||
/// </summary>
|
||||
public bool? IsChecked
|
||||
{
|
||||
get => isChecked;
|
||||
set
|
||||
{
|
||||
if (!initialized)
|
||||
{
|
||||
InitialState = value;
|
||||
initialized = true;
|
||||
isChecked = value; // don't remove
|
||||
|
||||
// the iteration over an Collection triggers the notification
|
||||
// of the "IsChecked" property and slows the performance of the loop,
|
||||
// the return prevents the OnPropertyChanged
|
||||
// notification at initialization
|
||||
return;
|
||||
}
|
||||
|
||||
// raise event to update the date tree, see FilterCommon class
|
||||
// only type date type fields are subscribed to the OnDateStatusChanged event
|
||||
// OnDateStatusChanged is not triggered at tree initialization
|
||||
if (FieldType == typeof(DateTime))
|
||||
{
|
||||
OnDateStatusChanged?.Invoke(this, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
isChecked = value;
|
||||
OnPropertyChanged("IsChecked");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Content displayed
|
||||
/// </summary>
|
||||
public string Label { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Hierarchical level for the date
|
||||
/// </summary>
|
||||
public int Level { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Parent of lower levels (days, months)
|
||||
/// </summary>
|
||||
public FilterItem Parent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Set the state of the IsChecked property for date, does not invoke the update of the tree
|
||||
/// </summary>
|
||||
public bool? SetState
|
||||
{
|
||||
get => isChecked;
|
||||
set
|
||||
{
|
||||
isChecked = value;
|
||||
|
||||
if (!initialized)
|
||||
{
|
||||
InitialState = value;
|
||||
initialized = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
OnPropertyChanged("IsChecked");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the initial state has changed
|
||||
/// </summary>
|
||||
public bool Changed => isChecked != InitialState;
|
||||
|
||||
#endregion Public Properties
|
||||
}
|
||||
}
|
||||
235
FSI.Lib/Wpf/Ctrls/FilterDataGrid/Loc.cs
Normal file
235
FSI.Lib/Wpf/Ctrls/FilterDataGrid/Loc.cs
Normal file
@@ -0,0 +1,235 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
|
||||
namespace FSI.Lib.Wpf.Ctrls.FilterDataGrid
|
||||
{
|
||||
public enum Local
|
||||
{
|
||||
Chinese,
|
||||
Dutch,
|
||||
English,
|
||||
French,
|
||||
German,
|
||||
Italian,
|
||||
Russian,
|
||||
}
|
||||
|
||||
public class Loc
|
||||
{
|
||||
#region Private Fields
|
||||
|
||||
private Local language;
|
||||
|
||||
// culture name(used for dates)
|
||||
private static readonly Dictionary<Local, string> CultureNames = new Dictionary<Local, string>
|
||||
{
|
||||
{ Local.Chinese, "zh-Hans" },
|
||||
{ Local.Dutch, "nl-NL" },
|
||||
{ Local.English, "en-US" },
|
||||
{ Local.French, "fr-FR" },
|
||||
{ Local.German, "de-DE" },
|
||||
{ Local.Italian, "it-IT" },
|
||||
{ Local.Russian, "ru-RU" },
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Translation dictionary
|
||||
/// </summary>
|
||||
private static readonly Dictionary<string, Dictionary<Local, string>> Translation =
|
||||
new Dictionary<string, Dictionary<Local, string>>
|
||||
{
|
||||
{
|
||||
"All", new Dictionary<Local, string>
|
||||
{
|
||||
{ Local.Chinese, "(全选)" },
|
||||
{ Local.Dutch, "(Alles selecteren)" },
|
||||
{ Local.English, "(Select all)" },
|
||||
{ Local.French, "(Sélectionner tout)" },
|
||||
{ Local.German, "(Alle auswählen)" },
|
||||
{ Local.Italian, "(Seleziona tutto)" },
|
||||
{ Local.Russian, "(Выбрать все)" },
|
||||
}
|
||||
},
|
||||
{
|
||||
"Empty", new Dictionary<Local, string>
|
||||
{
|
||||
{ Local.Chinese, "(空白)" },
|
||||
{ Local.Dutch, "(Leeg)" },
|
||||
{ Local.English, "(Blank)" },
|
||||
{ Local.French, "(Vides)" },
|
||||
{ Local.German, "(Leer)" },
|
||||
{ Local.Italian, "(Vuoto)" },
|
||||
{ Local.Russian, "(Заготовки)" },
|
||||
}
|
||||
},
|
||||
{
|
||||
"Clear", new Dictionary<Local, string>
|
||||
{
|
||||
{ Local.Chinese, "清除过滤器 \"{0}\"" },
|
||||
{ Local.Dutch, "Filter \"{0}\" verwijderen" },
|
||||
{ Local.English, "Clear filter \"{0}\"" },
|
||||
{ Local.French, "Effacer le filtre \"{0}\"" },
|
||||
{ Local.German, "Filter löschen \"{0}\"" },
|
||||
{ Local.Italian, "Cancella filtro \"{0}\"" },
|
||||
{ Local.Russian, "Очистить фильтр \"{0}\"" },
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"Contains", new Dictionary<Local, string>
|
||||
{
|
||||
{ Local.Chinese, "搜索(包含)" },
|
||||
{ Local.Dutch, "Zoek (bevat)" },
|
||||
{ Local.English, "Search (contains)" },
|
||||
{ Local.French, "Rechercher (contient)" },
|
||||
{ Local.German, "Suche (enthält)" },
|
||||
{ Local.Italian, "Cerca (contiene)" },
|
||||
{ Local.Russian, "Искать (содержит)" },
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"StartsWith", new Dictionary<Local, string>
|
||||
{
|
||||
{ Local.Chinese, "搜索 (来自)" },
|
||||
{ Local.Dutch, "Zoek (beginnen met)" },
|
||||
{ Local.English, "Search (startswith)" },
|
||||
{ Local.French, "Rechercher (commence par)" },
|
||||
{ Local.German, "Suche (beginnen mit)" },
|
||||
{ Local.Italian, "Cerca (inizia con)" },
|
||||
{ Local.Russian, "Искать (hачни с)" },
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"Toggle", new Dictionary<Local, string>
|
||||
{
|
||||
{ Local.Chinese, "切換包含/開始於" },
|
||||
{ Local.Dutch, "Toggle bevat/begint met" },
|
||||
{ Local.English, "Toggle contains/startswith" },
|
||||
{ Local.French, "Basculer contient/commence par" },
|
||||
{ Local.German, "Toggle enthält/beginnt mit" },
|
||||
{ Local.Italian, "Toggle contiene/inizia con" },
|
||||
{ Local.Russian, "Переключить содержит/начинается с" },
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"Ok", new Dictionary<Local, string>
|
||||
{
|
||||
{ Local.Chinese, "确定" },
|
||||
{ Local.Dutch, "Ok" },
|
||||
{ Local.English, "Ok" },
|
||||
{ Local.French, "Ok" },
|
||||
{ Local.German, "Ok" },
|
||||
{ Local.Italian, "Ok" },
|
||||
{ Local.Russian, "Ok" },
|
||||
}
|
||||
},
|
||||
{
|
||||
"Cancel", new Dictionary<Local, string>
|
||||
{
|
||||
{ Local.Chinese, "取消" },
|
||||
{ Local.Dutch, "Annuleren" },
|
||||
{ Local.English, "Cancel" },
|
||||
{ Local.French, "Annuler" },
|
||||
{ Local.German, "Abbrechen" },
|
||||
{ Local.Italian, "Annulla" },
|
||||
{ Local.Russian, "Отмена" },
|
||||
}
|
||||
},
|
||||
{
|
||||
"Status", new Dictionary<Local, string>
|
||||
{
|
||||
{ Local.Chinese, "{0:n0} 找到了 {1:n0} 条记录" },
|
||||
{ Local.Dutch, "{0:n0} rij(en) gevonden op {1:n0}" },
|
||||
{ Local.English, "{0:n0} record(s) found on {1:n0}" },
|
||||
{ Local.French, "{0:n0} enregistrement(s) trouvé(s) sur {1:n0}" },
|
||||
{ Local.German, "{0:n0} zeilen angezeigt von {1:n0}" },
|
||||
{ Local.Italian, "{0:n0} oggetti trovati su {1:n0}" },
|
||||
{ Local.Russian, "{0:n0} записей найдено на {1:n0}" },
|
||||
}
|
||||
},
|
||||
{
|
||||
"ElapsedTime", new Dictionary<Local, string>
|
||||
{
|
||||
{ Local.Chinese, "经过时间{0:mm}:{0:ss}.{0:ff}" },
|
||||
{ Local.Dutch, "Verstreken tijd {0:mm}:{0:ss}.{0:ff}" },
|
||||
{ Local.English, "Elapsed time {0:mm}:{0:ss}.{0:ff}" },
|
||||
{ Local.French, "Temps écoulé {0:mm}:{0:ss}.{0:ff}" },
|
||||
{ Local.German, "Verstrichene Zeit {0:mm}:{0:ss}.{0:ff}" },
|
||||
{ Local.Italian, "Tempo trascorso {0:mm}:{0:ss}.{0:ff}" },
|
||||
{ Local.Russian, "Пройденное время {0:mm}:{0:ss}.{0:ff}" },
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endregion Private Fields
|
||||
|
||||
#region Constructors
|
||||
|
||||
public Loc()
|
||||
{
|
||||
Language = Local.English;
|
||||
}
|
||||
|
||||
#endregion Constructors
|
||||
|
||||
#region Public Properties
|
||||
|
||||
public Local Language
|
||||
{
|
||||
get => language;
|
||||
set
|
||||
{
|
||||
language = value;
|
||||
Culture = new CultureInfo(CultureNames[value]);
|
||||
}
|
||||
}
|
||||
|
||||
public CultureInfo Culture { get; private set; }
|
||||
|
||||
public string CultureName => CultureNames[Language];
|
||||
|
||||
public string LanguageName => Enum.GetName(typeof(Local), Language);
|
||||
|
||||
public string All => Translate("All");
|
||||
|
||||
public string Cancel => Translate("Cancel");
|
||||
|
||||
public string Clear => Translate("Clear");
|
||||
|
||||
public string Contains => Translate("Contains");
|
||||
|
||||
public string ElapsedTime => Translate("ElapsedTime");
|
||||
|
||||
public string Empty => Translate("Empty");
|
||||
|
||||
public string Ok => Translate("Ok");
|
||||
|
||||
public string StartsWith => Translate("StartsWith");
|
||||
|
||||
public string Status => Translate("Status");
|
||||
|
||||
public string Toggle => Translate("Toggle");
|
||||
|
||||
#endregion Public Properties
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Translated into the language
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
private string Translate(string key)
|
||||
{
|
||||
return Translation.ContainsKey(key) && Translation[key].ContainsKey(Language)
|
||||
? Translation[key][Language]
|
||||
: "unknow";
|
||||
}
|
||||
|
||||
#endregion Private Methods
|
||||
}
|
||||
}
|
||||
1
FSI.Lib/Wpf/Ctrls/FilterDataGrid/Readme.md
Normal file
1
FSI.Lib/Wpf/Ctrls/FilterDataGrid/Readme.md
Normal file
@@ -0,0 +1 @@
|
||||
https://github.com/macgile/DataGridFilter
|
||||
36
FSI.Lib/Wpf/Ctrls/FilterDataGrid/StringFormatConverter.cs
Normal file
36
FSI.Lib/Wpf/Ctrls/FilterDataGrid/StringFormatConverter.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace FSI.Lib.Wpf.Ctrls.FilterDataGrid
|
||||
{
|
||||
public class StringFormatConverter : IMultiValueConverter
|
||||
{
|
||||
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
try
|
||||
{
|
||||
// values [0] contains the format
|
||||
if (values[0] == DependencyProperty.UnsetValue || string.IsNullOrEmpty(values[0]?.ToString()))
|
||||
return string.Empty;
|
||||
|
||||
var stringFormat = values[0].ToString();
|
||||
|
||||
return string.Format(stringFormat, values.Skip(1).ToArray());
|
||||
}
|
||||
catch (FormatException ex)
|
||||
{
|
||||
Debug.WriteLine($"StringFormatConverter.Convert error: {ex.Message}");
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
880
FSI.Lib/Wpf/Ctrls/FilterDataGrid/Themes/FilterDataGrid.xaml
Normal file
880
FSI.Lib/Wpf/Ctrls/FilterDataGrid/Themes/FilterDataGrid.xaml
Normal file
@@ -0,0 +1,880 @@
|
||||
<ResourceDictionary x:Class="FSI.Lib.Wpf.Ctrls.FilterDataGrid.FilterDataGridDictionary"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:control="clr-namespace:FSI.Lib.Wpf.Ctrls.FilterDataGrid"
|
||||
xmlns:sys="clr-namespace:System;assembly=mscorlib">
|
||||
|
||||
<!-- STRING FORMAT CONVERTER -->
|
||||
<control:StringFormatConverter x:Key="StringFormatConverter" />
|
||||
|
||||
<!-- INITIAL POPUP SIZE -->
|
||||
<sys:Double x:Key="PopupHeight">420</sys:Double>
|
||||
<sys:Double x:Key="PopupWidth">262</sys:Double>
|
||||
<sys:Boolean x:Key="StayOpen">False</sys:Boolean>
|
||||
|
||||
<!-- https://yqnn.github.io/svg-path-editor/ -->
|
||||
|
||||
<!-- FILTER SET ICON -->
|
||||
<Geometry x:Key="FilterSet">
|
||||
M 0 17 H 12 L 6 25 Z M 6 0 H 29 L 29 3 L 20 10 L 20 21 H 15 L 15 10 L 6 3 Z
|
||||
</Geometry>
|
||||
|
||||
<!-- FILTER BUTTON ICON -->
|
||||
<Geometry x:Key="Filter">
|
||||
M 7 10 L 12 15 L 17 10 H 7 Z
|
||||
</Geometry>
|
||||
|
||||
<!-- DELETE FILTER ICON -->
|
||||
<Geometry x:Key="FilterDelete">
|
||||
M11.1 11.4L8.5 8.9L9.8 7.6L12.3 10.1L14.8 7.6L16.1 8.9L13.6 11.4L16.1 13.9L14.8 15.2L12.3 12.6L9.8 15.2L8.5 13.9ZM0 0H13L13 2L8 6V14L5 11V6L0 2Z
|
||||
</Geometry>
|
||||
|
||||
<!-- BOX CHECKED ICON -->
|
||||
<Geometry x:Key="FilterChecked">
|
||||
M 125 125 L 0 125 V 0 H 125 Z M 1 124 H 124 V 1 H 1 Z M 20 68 L 29 57 L 56 80 L 98 25 L 110 35 L 59 101 Z
|
||||
</Geometry>
|
||||
|
||||
<!-- GRIPSIZE ICON -->
|
||||
<Geometry x:Key="GripSizeIcon">
|
||||
M0 9L2 9M4 9L6 9M8 9L10 9M1 8L1 10M5 8L5 10M9 8L9 10M4 5L6 5M8 5L10 5M5 4L5 6M9 4L9 6M8 1L10 1M9 0L9 2
|
||||
</Geometry>
|
||||
|
||||
<!-- SEARCH MAGNIFIER ICON -->
|
||||
<Geometry x:Key="Magnifier">
|
||||
M9.6 8.5H9L8.7 8.2C9.6 7.4 10 6.2 10 5C10 2.2 7.8 0 5 0S0 2.2 0 5S2.2 10 5 10C6.2 10 7.4 9.6 8.2 8.7L8.5
|
||||
9V9.6L12.3 13.5L13.5 12.3L9.6 8.5ZM5 8.5C3.1 8.5 1.5 6.9 1.5 5S3.1 1.5 5 1.5S8.5 3.1 8.5 5S6.9 8.5 5 8.5Z
|
||||
</Geometry>
|
||||
|
||||
<!-- SEARCH DELETE ICON -->
|
||||
<Geometry x:Key="Delete">
|
||||
M 0 0 M 2 3 L 3 2 L 8 7 L 13 2 L 14 3 L 9 8 L 14 13 L 13 14 L 8 9 L 3 14 L 2 13 L 7 8 Z M 16 0 M 0 16 M 16 16
|
||||
</Geometry>
|
||||
|
||||
<!-- SEARCH CONTAINS ICON -->
|
||||
<Geometry x:Key="StartsWith">
|
||||
M 0.6 1.3 V 0 C 1.1 -0.1 5.5 -1.7 6.2 1.6 V 7.2 H 5 V 6.2 C 4.5 6.7 4 7 3.5 7.2 H 1.4 C 0.4 6.8 0.1 6.1 0 5.2 C 0 4.7 0 3 3 2.8 H 4.9 V 1.7
|
||||
C 4.4 0.2 2.3 0.6 1 1.1 Z M 4.9 5.1 V 3.8 H 2.6 V 3.8 C 1 3.9 0.9 6.2 2.6 6.2 C 3.5 6.2 4 5.9 4.7 5.3 Z M 9.3 0.9 L 9.5 3.5 L 7.4 2 L 6.9 2.9
|
||||
L 9.2 4 L 6.9 5.1 L 7.3 6.1 L 9.5 4.7 L 9.3 7.2 H 10.4 L 10.2 4.6 L 12.3 6 L 12.8 5.1 L 10.5 4.1 L 12.8 2.9 L 12.3 2 L 10.2 3.5 L 10.4 0.9 Z
|
||||
M 16.5 0.9 H 17.6 L 17.4 3.4 L 19.5 2 L 20 2.9 L 17.7 4 L 20 5.2 L 19.5 6 L 17.4 4.7 L 17.6 7.2 H 16.5 L 16.6 4.7 L 14.5 6 L 14 5.2 L 16.3 4
|
||||
L 14 2.9 L 14.6 2 L 16.7 3.4 Z
|
||||
</Geometry>
|
||||
|
||||
<!-- SEARCH STARTSWITH ICON -->
|
||||
<Geometry x:Key="Contains">
|
||||
M 7.2 1.3 V 0 C 7.7 -0.1 12.1 -1.7 12.8 1.6 V 7.2 H 11.6 V 6.2 C 11.1 6.7 10.6 7 10.1 7.2 H 8 C 7 6.8 6.7 6.1 6.6 5.2 C 6.6 4.7 6.6 3 9.6 2.8
|
||||
H 11.5 V 1.7 C 11 0.2 8.9 0.6 7.6 1.1 Z M 11.5 5.1 V 3.8 H 9.2 C 7.5 4.1 8.1 6.2 9.2 6.2 C 10.1 6.2 10.6 5.9 11.3 5.3 Z M 2.4 1 L 2.6 3.5
|
||||
L 0.5 2.1 L 0 3 L 2.2 4.1 L 0 5.3 L 0.4 6.1 L 2.6 4.7 L 2.4 7.2 H 3.5 L 3.3 4.7 L 5.4 6.1 L 5.9 5.2 L 3.6 4.1 L 5.9 3 L 5.4 2.1 L 3.3 3.5
|
||||
L 3.5 1 Z M 16.5 0.9 H 17.6 L 17.4 3.5 L 19.5 2 L 20 3 L 17.7 4 L 20 5.2 L 19.5 6 L 17.4 4.7 L 17.6 7.2 H 16.5 L 16.6 4.7 L 14.5 6 L 14 5.2
|
||||
L 16.3 4.1 L 14 2.9 L 14.6 2 L 16.6 3.4 Z
|
||||
</Geometry>
|
||||
|
||||
<!-- PLACEHOLDER SEARCH BOX -->
|
||||
<Style x:Key="PlaceHolder"
|
||||
TargetType="{x:Type TextBox}">
|
||||
<Setter Property="Background"
|
||||
Value="Transparent" />
|
||||
<Setter Property="BorderBrush"
|
||||
Value="Transparent" />
|
||||
<Setter Property="BorderThickness"
|
||||
Value="0" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate x:Name="SearchControlTemplate"
|
||||
TargetType="{x:Type TextBox}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- SEARCH TEXTBOX -->
|
||||
<TextBox x:Name="TextSource"
|
||||
Grid.Column="0"
|
||||
Margin="{TemplateBinding Margin}"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalAlignment}"
|
||||
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
Panel.ZIndex="2"
|
||||
Background="Transparent"
|
||||
BorderBrush="Transparent"
|
||||
BorderThickness="0"
|
||||
Focusable="True"
|
||||
FontSize="{TemplateBinding FontSize}"
|
||||
FontWeight="{TemplateBinding FontWeight}"
|
||||
MaxLength="{TemplateBinding MaxLength}"
|
||||
Text="{Binding Path=Text, RelativeSource={RelativeSource TemplatedParent}, UpdateSourceTrigger=PropertyChanged}" />
|
||||
<TextBox x:Name="TextBoxPlaceHolder"
|
||||
Grid.Column="0"
|
||||
Margin="{TemplateBinding Margin}"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalAlignment}"
|
||||
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
Panel.ZIndex="1"
|
||||
Background="Transparent"
|
||||
BorderBrush="Transparent"
|
||||
BorderThickness="0"
|
||||
FontSize="{TemplateBinding FontSize}"
|
||||
FontWeight="{TemplateBinding FontWeight}"
|
||||
MaxLength="{TemplateBinding MaxLength}"
|
||||
Text="{TemplateBinding Tag}">
|
||||
<TextBox.Style>
|
||||
<Style TargetType="{x:Type TextBox}">
|
||||
<Setter Property="Foreground"
|
||||
Value="Transparent" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding Path=Text, ElementName=TextSource}"
|
||||
Value="">
|
||||
<Setter Property="Foreground"
|
||||
Value="LightGray" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBox.Style>
|
||||
</TextBox>
|
||||
|
||||
<!-- BUTTON CLEAR FILTER -->
|
||||
<Button x:Name="ClearSearchBoxBtn"
|
||||
Grid.Column="1"
|
||||
Margin="2"
|
||||
Background="Transparent"
|
||||
Command="{x:Static control:FilterDataGrid.ClearSearchBox}"
|
||||
SnapsToDevicePixels="True"
|
||||
UseLayoutRounding="True">
|
||||
<Button.Style>
|
||||
<Style TargetType="{x:Type Button}">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border Background="Transparent"
|
||||
SnapsToDevicePixels="True">
|
||||
<!-- MAGNIFIER / DELETE ICON -->
|
||||
<Path x:Name="PathButton"
|
||||
Width="18"
|
||||
Height="18"
|
||||
Margin="0"
|
||||
Data="{StaticResource Delete}"
|
||||
Fill="DarkSlateGray"
|
||||
SnapsToDevicePixels="True"
|
||||
Stretch="Uniform"
|
||||
UseLayoutRounding="True" />
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<DataTrigger Binding="{Binding ElementName=TextSource, Path=Text}"
|
||||
Value="">
|
||||
<Setter Property="IsEnabled"
|
||||
Value="False" />
|
||||
<Setter TargetName="PathButton"
|
||||
Property="Data"
|
||||
Value="{StaticResource Magnifier}" />
|
||||
</DataTrigger>
|
||||
<Trigger Property="IsMouseOver"
|
||||
Value="True">
|
||||
<Setter Property="Cursor"
|
||||
Value="Hand" />
|
||||
<Setter TargetName="PathButton"
|
||||
Property="Fill"
|
||||
Value="Red" />
|
||||
<Setter TargetName="PathButton"
|
||||
Property="Stroke"
|
||||
Value="Red" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</Button.Style>
|
||||
</Button>
|
||||
|
||||
<!-- SEPARATOR -->
|
||||
<Border Grid.Column="2"
|
||||
Width="1"
|
||||
Margin="2,0,2,0"
|
||||
Background="LightGray" />
|
||||
|
||||
<!-- TOGGLE BUTTON -->
|
||||
<ToggleButton Name="SearchToggleButton"
|
||||
Grid.Column="3"
|
||||
Margin="2,0,2,0"
|
||||
VerticalAlignment="Stretch"
|
||||
Background="Transparent"
|
||||
DataContext="{Binding RelativeSource={RelativeSource AncestorType={x:Type control:FilterDataGrid}}}"
|
||||
IsChecked="{Binding StartsWith, UpdateSourceTrigger=PropertyChanged}"
|
||||
ToolTip="{Binding Translate.Toggle}">
|
||||
<ToggleButton.Style>
|
||||
<Style TargetType="{x:Type ToggleButton}">
|
||||
<Setter Property="Cursor"
|
||||
Value="Hand" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="ToggleButton">
|
||||
<Border Padding="3"
|
||||
Background="Transparent"
|
||||
BorderBrush="LightGray"
|
||||
BorderThickness="0">
|
||||
<Path x:Name="PathToggle"
|
||||
VerticalAlignment="Center"
|
||||
Data="{StaticResource Contains}"
|
||||
Fill="DarkSlateGray"
|
||||
Stretch="UniformToFill" />
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsChecked"
|
||||
Value="True">
|
||||
<Setter TargetName="PathToggle"
|
||||
Property="Data"
|
||||
Value="{StaticResource StartsWith}" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsMouseOver"
|
||||
Value="True">
|
||||
<Setter TargetName="PathToggle"
|
||||
Property="Fill"
|
||||
Value="Black" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ToggleButton.Style>
|
||||
</ToggleButton>
|
||||
</Grid>
|
||||
<!-- PLACEHOLDER TEXT -->
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsFocused"
|
||||
Value="True">
|
||||
<Setter TargetName="TextSource"
|
||||
Property="FocusManager.FocusedElement"
|
||||
Value="{Binding RelativeSource={RelativeSource Self}}" />
|
||||
</Trigger>
|
||||
<DataTrigger Binding="{Binding ElementName=SearchToggleButton, Path=IsChecked}"
|
||||
Value="True">
|
||||
<Setter TargetName="TextBoxPlaceHolder"
|
||||
Property="Text"
|
||||
Value="{Binding Translate.StartsWith, Mode=OneWay}" />
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding ElementName=SearchToggleButton, Path=IsChecked}"
|
||||
Value="False">
|
||||
<Setter TargetName="TextBoxPlaceHolder"
|
||||
Property="Text"
|
||||
Value="{Binding Translate.Contains, Mode=OneWay}" />
|
||||
</DataTrigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!-- DEFAULT STYLE AND CONTROLTEMPLATE FOR DATAGRID -->
|
||||
<Style BasedOn="{StaticResource {x:Type DataGrid}}"
|
||||
TargetType="{x:Type control:FilterDataGrid}">
|
||||
|
||||
<!-- DISABLING CanUserAddRows : AggregateException when user can add row -->
|
||||
<Setter Property="CanUserAddRows"
|
||||
Value="False" />
|
||||
|
||||
<!-- ROWS COUNT TEMPLATE -->
|
||||
<Setter Property="RowHeaderTemplate">
|
||||
<Setter.Value>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type DataGridRow}}, Path=Header}" />
|
||||
</DataTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type DataGrid}">
|
||||
<Border Padding="{TemplateBinding Padding}"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
SnapsToDevicePixels="True">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- CONTENT DATAGRID -->
|
||||
<ScrollViewer x:Name="DG_ScrollViewer"
|
||||
Grid.Row="0"
|
||||
CanContentScroll="True"
|
||||
Focusable="false">
|
||||
<ScrollViewer.Template>
|
||||
<ControlTemplate TargetType="{x:Type ScrollViewer}">
|
||||
<Grid ShowGridLines="False">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Button Width="{Binding CellsPanelHorizontalOffset, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"
|
||||
Command="{x:Static DataGrid.SelectAllCommand}"
|
||||
Focusable="false"
|
||||
Style="{DynamicResource {ComponentResourceKey ResourceId=DataGridSelectAllButtonStyle,
|
||||
TypeInTargetAssembly={x:Type DataGrid}}}"
|
||||
Visibility="{Binding HeadersVisibility, ConverterParameter={x:Static DataGridHeadersVisibility.All}, Converter={x:Static DataGrid.HeadersVisibilityConverter}, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" />
|
||||
<DataGridColumnHeadersPresenter x:Name="PART_ColumnHeadersPresenter"
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Visibility="{Binding HeadersVisibility, ConverterParameter={x:Static DataGridHeadersVisibility.Column}, Converter={x:Static DataGrid.HeadersVisibilityConverter}, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" />
|
||||
<ScrollContentPresenter x:Name="PART_ScrollContentPresenter"
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="2"
|
||||
CanContentScroll="{TemplateBinding CanContentScroll}" />
|
||||
<ScrollBar x:Name="PART_VerticalScrollBar"
|
||||
Grid.Row="1"
|
||||
Grid.Column="2"
|
||||
Maximum="{TemplateBinding ScrollableHeight}"
|
||||
Orientation="Vertical"
|
||||
ViewportSize="{TemplateBinding ViewportHeight}"
|
||||
Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"
|
||||
Value="{Binding VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" />
|
||||
<Grid Grid.Row="2"
|
||||
Grid.Column="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="{Binding NonFrozenColumnsViewportHorizontalOffset, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ScrollBar x:Name="PART_HorizontalScrollBar"
|
||||
Grid.Column="1"
|
||||
Maximum="{TemplateBinding ScrollableWidth}"
|
||||
Orientation="Horizontal"
|
||||
ViewportSize="{TemplateBinding ViewportWidth}"
|
||||
Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"
|
||||
Value="{Binding HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</ScrollViewer.Template>
|
||||
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
|
||||
</ScrollViewer>
|
||||
|
||||
<!-- STATUS BAR & RESULT FILTER -->
|
||||
<Border x:Name="BorderStatusBar"
|
||||
Grid.Row="1"
|
||||
Padding="4,2"
|
||||
Background="Transparent"
|
||||
BorderBrush="LightGray"
|
||||
BorderThickness="0,1">
|
||||
<Border.Style>
|
||||
<Style TargetType="Border">
|
||||
<Setter Property="Visibility"
|
||||
Value="Collapsed" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding Path=ShowStatusBar, RelativeSource={RelativeSource AncestorType={x:Type control:FilterDataGrid}}}"
|
||||
Value="True">
|
||||
<Setter Property="Visibility"
|
||||
Value="Visible" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Border.Style>
|
||||
|
||||
<UniformGrid Columns="2"
|
||||
DataContext="{Binding RelativeSource={RelativeSource AncestorType={x:Type control:FilterDataGrid}}}">
|
||||
|
||||
<!-- RESULT STATUS -->
|
||||
<TextBlock HorizontalAlignment="Left">
|
||||
<TextBlock.Text>
|
||||
<MultiBinding Converter="{StaticResource StringFormatConverter}">
|
||||
<Binding Path="Translate.Status" />
|
||||
<Binding Path="Items.Count"
|
||||
UpdateSourceTrigger="PropertyChanged" />
|
||||
<Binding Path="ItemsSourceCount" />
|
||||
</MultiBinding>
|
||||
</TextBlock.Text>
|
||||
</TextBlock>
|
||||
|
||||
<!-- ELAPSED TIME -->
|
||||
<TextBlock HorizontalAlignment="Right">
|
||||
<TextBlock.Text>
|
||||
<MultiBinding Converter="{StaticResource StringFormatConverter}">
|
||||
<Binding Path="Translate.ElapsedTime"
|
||||
UpdateSourceTrigger="PropertyChanged" />
|
||||
<Binding Path="ElapsedTime" />
|
||||
</MultiBinding>
|
||||
</TextBlock.Text>
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="Visibility"
|
||||
Value="Collapsed" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding ShowElapsedTime}"
|
||||
Value="True">
|
||||
<Setter Property="Visibility"
|
||||
Value="Visible" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
</TextBlock>
|
||||
</UniformGrid>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!-- DATAGRIDCOLUMNHEADER STYLE -->
|
||||
<Style BasedOn="{StaticResource {x:Type DataGridColumnHeader}}"
|
||||
TargetType="DataGridColumnHeader">
|
||||
<Setter Property="HorizontalContentAlignment"
|
||||
Value="Stretch" />
|
||||
</Style>
|
||||
|
||||
<!-- DATATEMPLATE OF DATAGRIDCOLUMNHEADER -->
|
||||
<DataTemplate x:Key="DataGridHeaderTemplate">
|
||||
|
||||
<!-- HEADER STRECH TO CONTENTPRESENTER OF DATAGRIDCOLUMNHEADER -->
|
||||
<Grid x:Name="ContainerFilterGrid"
|
||||
Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type ContentPresenter}, Mode=FindAncestor, AncestorLevel=1}, Path=ActualWidth}"
|
||||
HorizontalAlignment="Stretch"
|
||||
Background="Transparent">
|
||||
|
||||
<!-- HEADER/BUTTON -->
|
||||
<Grid x:Name="GridHeaderButton"
|
||||
ShowGridLines="False">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- RENDER THE HEADER TEXT -->
|
||||
<TextBlock Grid.Column="0"
|
||||
Text="{Binding}" />
|
||||
|
||||
<!-- FILTER BUTTON -->
|
||||
<Button Name="filterButton"
|
||||
Grid.Column="1"
|
||||
Width="19"
|
||||
Height="19"
|
||||
Background="Transparent"
|
||||
BorderBrush="DarkGray"
|
||||
BorderThickness="1"
|
||||
Command="{x:Static control:FilterDataGrid.ShowFilter}"
|
||||
Cursor="Hand"
|
||||
Opacity="0.5"
|
||||
OverridesDefaultStyle="True"
|
||||
SnapsToDevicePixels="True"
|
||||
UseLayoutRounding="True">
|
||||
<Button.Style>
|
||||
<Style TargetType="{x:Type Button}">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type Button}">
|
||||
<Border Padding="2"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="1"
|
||||
SnapsToDevicePixels="True"
|
||||
UseLayoutRounding="True">
|
||||
<Path x:Name="PathFilterIcon"
|
||||
Margin="0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Data="{StaticResource Filter}"
|
||||
Fill="DarkSlateGray"
|
||||
Stretch="Uniform" />
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</Button.Style>
|
||||
</Button>
|
||||
</Grid>
|
||||
|
||||
<!-- POPUP -->
|
||||
<Popup Name="FilterPopup"
|
||||
AllowsTransparency="True"
|
||||
IsOpen="False"
|
||||
PlacementTarget="{Binding ElementName=ContainerFilterGrid}"
|
||||
StaysOpen="{StaticResource StayOpen}">
|
||||
|
||||
<Border x:Name="PopUpBorder"
|
||||
Padding="0"
|
||||
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
|
||||
BorderBrush="LightGray"
|
||||
BorderThickness="1">
|
||||
|
||||
<Grid x:Name="SizableContentGrid"
|
||||
MinWidth="{StaticResource PopupWidth}"
|
||||
MinHeight="{StaticResource PopupHeight}"
|
||||
ShowGridLines="False"
|
||||
ZIndex="1">
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"
|
||||
MinWidth="32" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- BUTTON CLEAR FILTER -->
|
||||
<Button x:Name="ClearFilterBnt"
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="2"
|
||||
Margin="2,10,2,2"
|
||||
Padding="4"
|
||||
HorizontalAlignment="Stretch"
|
||||
Command="{x:Static control:FilterDataGrid.RemoveFilter}"
|
||||
Content="{Binding Path=Content, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type DataGridColumnHeader}}, UpdateSourceTrigger=PropertyChanged}"
|
||||
FontSize="13"
|
||||
OverridesDefaultStyle="True">
|
||||
<Button.Style>
|
||||
<Style TargetType="{x:Type Button}">
|
||||
<Setter Property="Foreground"
|
||||
Value="DarkSlateGray" />
|
||||
<Setter Property="SnapsToDevicePixels"
|
||||
Value="True" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type Button}">
|
||||
<Border x:Name="BorderContent"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
BorderBrush="Transparent"
|
||||
BorderThickness="0"
|
||||
SnapsToDevicePixels="True"
|
||||
UseLayoutRounding="True">
|
||||
<Grid x:Name="ContentGrid"
|
||||
Background="Transparent">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Path Grid.Column="0"
|
||||
Width="16"
|
||||
Margin="0,0,10,0"
|
||||
Data="{StaticResource FilterDelete}"
|
||||
Fill="{TemplateBinding Foreground}"
|
||||
Stretch="Uniform" />
|
||||
|
||||
<TextBlock x:Name="ContentPresenter"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
DataContext="{Binding RelativeSource={RelativeSource AncestorType={x:Type control:FilterDataGrid}}}">
|
||||
<TextBlock.Text>
|
||||
<MultiBinding Converter="{StaticResource StringFormatConverter}">
|
||||
<MultiBinding.Bindings>
|
||||
<Binding Path="Translate.Clear"
|
||||
TargetNullValue=""
|
||||
UpdateSourceTrigger="PropertyChanged" />
|
||||
<Binding ElementName="ClearFilterBnt"
|
||||
Path="Content" />
|
||||
</MultiBinding.Bindings>
|
||||
</MultiBinding>
|
||||
</TextBlock.Text>
|
||||
</TextBlock>
|
||||
</Grid>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver"
|
||||
Value="True">
|
||||
<Setter TargetName="BorderContent"
|
||||
Property="Background"
|
||||
Value="#F0F0F0" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsPressed"
|
||||
Value="True">
|
||||
<Setter TargetName="BorderContent"
|
||||
Property="Background">
|
||||
<Setter.Value>
|
||||
<SolidColorBrush Opacity="0.8"
|
||||
Color="LightGray" />
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Trigger>
|
||||
<Trigger Property="IsEnabled"
|
||||
Value="False">
|
||||
<Setter Property="Foreground">
|
||||
<Setter.Value>
|
||||
<SolidColorBrush Opacity="0.5"
|
||||
Color="DarkSlateGray" />
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</Button.Style>
|
||||
</Button>
|
||||
|
||||
<!-- SEPARATOR -->
|
||||
<Separator Grid.Row="3"
|
||||
Grid.Column="1"
|
||||
Margin="0,2"
|
||||
Background="LightGray" />
|
||||
|
||||
<!-- SEARCH BOX -->
|
||||
<Border Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
Margin="0,4,4,2"
|
||||
Padding="0,2"
|
||||
VerticalAlignment="Top"
|
||||
Background="Transparent"
|
||||
BorderBrush="LightGray"
|
||||
BorderThickness="1">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBox x:Name="SearchBox"
|
||||
Grid.Column="0"
|
||||
Margin="0"
|
||||
Padding="2,0,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalContentAlignment="Center"
|
||||
AcceptsReturn="False"
|
||||
DataContext="{Binding RelativeSource={RelativeSource AncestorType={x:Type control:FilterDataGrid}}}"
|
||||
Focusable="True"
|
||||
FontSize="13"
|
||||
MaxLength="20"
|
||||
Style="{StaticResource PlaceHolder}"
|
||||
Tag="{Binding Translate.Contains}">
|
||||
<TextBox.InputBindings>
|
||||
<KeyBinding Key="Enter"
|
||||
Command="{x:Static control:FilterDataGrid.ApplyFilter}" />
|
||||
</TextBox.InputBindings>
|
||||
</TextBox>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- ICON (current filter is set) -->
|
||||
<Path x:Name="PathIsFiltered"
|
||||
Grid.Row="5"
|
||||
Grid.Column="0"
|
||||
Width="19"
|
||||
Height="20"
|
||||
Margin="0,5,0,0"
|
||||
VerticalAlignment="Top"
|
||||
Data="{StaticResource FilterChecked}"
|
||||
Fill="DarkSlateGray"
|
||||
Stretch="Fill"
|
||||
Stroke="DarkSlateGray"
|
||||
StrokeThickness="0.2">
|
||||
<Path.Style>
|
||||
<Style TargetType="Path">
|
||||
<Setter Property="Visibility"
|
||||
Value="Hidden" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding ElementName=ClearFilterBnt, Path=IsEnabled}"
|
||||
Value="True">
|
||||
<Setter Property="Visibility"
|
||||
Value="Visible" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Path.Style>
|
||||
</Path>
|
||||
|
||||
<!-- LISTBOX / TREEVIEW -->
|
||||
<Border Grid.Row="5"
|
||||
Grid.Column="1"
|
||||
Margin="0,4,4,4"
|
||||
BorderThickness="0">
|
||||
|
||||
<Grid x:Name="GridItemControl">
|
||||
|
||||
<ListBox x:Name="PopupListBox"
|
||||
Grid.Row="0"
|
||||
Padding="2"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
BorderBrush="LightGray"
|
||||
BorderThickness="1"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Auto"
|
||||
ScrollViewer.VerticalScrollBarVisibility="Auto"
|
||||
Visibility="Collapsed">
|
||||
<ListBox.ItemContainerStyle>
|
||||
<Style BasedOn="{StaticResource {x:Type ListBoxItem}}"
|
||||
TargetType="{x:Type ListBoxItem}">
|
||||
<Setter Property="HorizontalContentAlignment"
|
||||
Value="Left" />
|
||||
<Setter Property="VerticalContentAlignment"
|
||||
Value="Center" />
|
||||
</Style>
|
||||
</ListBox.ItemContainerStyle>
|
||||
<ListBox.ItemTemplate>
|
||||
<!--
|
||||
ContentStringFormat="{}{0}"
|
||||
Content="{Binding Label, ConverterCulture=en-US}"
|
||||
ContentStringFormat="F2, en-US"
|
||||
the value of the label is a string, so it cannot be formatted
|
||||
-->
|
||||
<DataTemplate DataType="{x:Type control:FilterItem}">
|
||||
<CheckBox x:Name="CheckBox"
|
||||
Width="Auto"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
Command="{x:Static control:FilterDataGrid.IsChecked}"
|
||||
CommandParameter="{Binding}"
|
||||
Content="{Binding Label}"
|
||||
FontWeight="Normal"
|
||||
IsChecked="{Binding IsChecked}"
|
||||
IsThreeState="False" />
|
||||
<DataTemplate.Triggers>
|
||||
<DataTrigger Binding="{Binding Level}"
|
||||
Value="1">
|
||||
<Setter TargetName="CheckBox"
|
||||
Property="Margin"
|
||||
Value="4,0,0,0" />
|
||||
</DataTrigger>
|
||||
</DataTemplate.Triggers>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
|
||||
<TreeView x:Name="PopupTreeview"
|
||||
Grid.Row="0"
|
||||
BorderBrush="LightGray"
|
||||
BorderThickness="1"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Auto"
|
||||
ScrollViewer.VerticalScrollBarVisibility="Auto"
|
||||
Visibility="Collapsed">
|
||||
<TreeView.ItemTemplate>
|
||||
<HierarchicalDataTemplate DataType="control:FilterItem"
|
||||
ItemsSource="{Binding Children}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<CheckBox x:Name="CheckBoxTree"
|
||||
VerticalAlignment="Center"
|
||||
Focusable="False"
|
||||
IsChecked="{Binding IsChecked, UpdateSourceTrigger=PropertyChanged}" />
|
||||
<ContentPresenter Margin="2"
|
||||
Content="{Binding Label}" />
|
||||
</StackPanel>
|
||||
<DataTemplate.Triggers>
|
||||
<DataTrigger Binding="{Binding Level}"
|
||||
Value="1">
|
||||
<Setter TargetName="CheckBoxTree"
|
||||
Property="Margin"
|
||||
Value="4,0,0,0" />
|
||||
</DataTrigger>
|
||||
</DataTemplate.Triggers>
|
||||
</HierarchicalDataTemplate>
|
||||
</TreeView.ItemTemplate>
|
||||
<TreeView.ItemContainerStyle>
|
||||
<Style BasedOn="{StaticResource {x:Type TreeViewItem}}"
|
||||
TargetType="{x:Type TreeViewItem}">
|
||||
<Setter Property="OverridesDefaultStyle"
|
||||
Value="True" />
|
||||
<Setter Property="IsExpanded"
|
||||
Value="False" />
|
||||
<Setter Property="HorizontalContentAlignment"
|
||||
Value="Stretch" />
|
||||
<Setter Property="VerticalContentAlignment"
|
||||
Value="Stretch" />
|
||||
<Setter Property="Visibility"
|
||||
Value="Visible" />
|
||||
</Style>
|
||||
</TreeView.ItemContainerStyle>
|
||||
</TreeView>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- OK/CANCEL BUTTON -->
|
||||
<UniformGrid Grid.Row="6"
|
||||
Grid.Column="1"
|
||||
Margin="0,6,4,6"
|
||||
HorizontalAlignment="Right"
|
||||
Background="Transparent"
|
||||
Columns="2"
|
||||
DataContext="{Binding RelativeSource={RelativeSource AncestorType={x:Type control:FilterDataGrid}}}">
|
||||
|
||||
<Button Width="100"
|
||||
Margin="0"
|
||||
HorizontalAlignment="Left"
|
||||
Command="{x:Static control:FilterDataGrid.ApplyFilter}"
|
||||
Content="{Binding Translate.Ok}" />
|
||||
|
||||
<Button Width="100"
|
||||
Margin="6,0,0,0"
|
||||
HorizontalAlignment="Right"
|
||||
Command="{x:Static control:FilterDataGrid.CancelFilter}"
|
||||
Content="{Binding Translate.Cancel}" />
|
||||
</UniformGrid>
|
||||
|
||||
<!-- RESIZE GRIP -->
|
||||
<Thumb x:Name="PopupThumb"
|
||||
Grid.Row="7"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="2"
|
||||
Width="20"
|
||||
Height="Auto"
|
||||
Margin="0,0,2,2"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Bottom"
|
||||
SnapsToDevicePixels="True"
|
||||
UseLayoutRounding="True">
|
||||
<Thumb.Style>
|
||||
<Style TargetType="{x:Type Thumb}">
|
||||
<Style.Setters>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate>
|
||||
<Grid x:Name="resizeVisual"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Bottom"
|
||||
Background="Transparent"
|
||||
DockPanel.Dock="Right"
|
||||
SnapsToDevicePixels="True"
|
||||
UseLayoutRounding="True">
|
||||
<Path Width="12"
|
||||
Height="12"
|
||||
Margin="0"
|
||||
Data="{StaticResource GripSizeIcon}"
|
||||
Stretch="None"
|
||||
Stroke="LightSlateGray"
|
||||
StrokeThickness="1" />
|
||||
<Grid.Style>
|
||||
<Style TargetType="{x:Type Grid}">
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsMouseOver"
|
||||
Value="True">
|
||||
<Setter Property="Cursor"
|
||||
Value="SizeNWSE" />
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Grid.Style>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style.Setters>
|
||||
</Style>
|
||||
</Thumb.Style>
|
||||
</Thumb>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Popup>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ResourceDictionary>
|
||||
18
FSI.Lib/Wpf/Ctrls/FilterDataGrid/Themes/Generic.xaml
Normal file
18
FSI.Lib/Wpf/Ctrls/FilterDataGrid/Themes/Generic.xaml
Normal file
@@ -0,0 +1,18 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:control="clr-namespace:FSI.Lib.Wpf.Ctrls.FilterDataGrid">
|
||||
|
||||
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="/FilterDataGrid;component/Themes/FilterDataGrid.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
<!--
|
||||
EXTERNAL ACCESS FOR CUSTOMIZE DEFAULT STYLE
|
||||
SEE FILTERDATAGRID DEFAULT STYLE
|
||||
-->
|
||||
<Style x:Key="{ComponentResourceKey {x:Type control:FilterDataGrid},
|
||||
FilterDataGridStyle}"
|
||||
BasedOn="{StaticResource {x:Type control:FilterDataGrid}}"
|
||||
TargetType="{x:Type control:FilterDataGrid}" />
|
||||
</ResourceDictionary>
|
||||
138
FSI.Lib/Wpf/WindowExtensions.cs
Normal file
138
FSI.Lib/Wpf/WindowExtensions.cs
Normal file
@@ -0,0 +1,138 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows;
|
||||
|
||||
namespace FSI.Lib.Wpf.ExtensionMethods
|
||||
{
|
||||
public static class WindowExtensions
|
||||
{
|
||||
#region Public Methods
|
||||
|
||||
public static bool ActivateCenteredToMouse(this Window window)
|
||||
{
|
||||
ComputeTopLeft(ref window);
|
||||
return window.Activate();
|
||||
}
|
||||
|
||||
public static void ShowCenteredToMouse(this Window window)
|
||||
{
|
||||
// in case the default start-up location isn't set to Manual
|
||||
WindowStartupLocation oldLocation = window.WindowStartupLocation;
|
||||
// set location to manual -> window will be placed by Top and Left property
|
||||
window.WindowStartupLocation = WindowStartupLocation.Manual;
|
||||
ComputeTopLeft(ref window);
|
||||
window.Show();
|
||||
window.WindowStartupLocation = oldLocation;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
private static void ComputeTopLeft(ref Window window)
|
||||
{
|
||||
W32Point pt = new W32Point();
|
||||
if (!GetCursorPos(ref pt))
|
||||
{
|
||||
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
|
||||
}
|
||||
|
||||
// 0x00000002: return nearest monitor if pt is not contained in any monitor.
|
||||
IntPtr monHandle = MonitorFromPoint(pt, 0x00000002);
|
||||
W32MonitorInfo monInfo = new W32MonitorInfo();
|
||||
monInfo.Size = Marshal.SizeOf(typeof(W32MonitorInfo));
|
||||
|
||||
if (!GetMonitorInfo(monHandle, ref monInfo))
|
||||
{
|
||||
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
|
||||
}
|
||||
|
||||
// use WorkArea struct to include the taskbar position.
|
||||
W32Rect monitor = monInfo.WorkArea;
|
||||
double offsetX = Math.Round(window.Width / 2);
|
||||
double offsetY = Math.Round(window.Height / 2);
|
||||
|
||||
double top = pt.Y - offsetY;
|
||||
double left = pt.X - offsetX;
|
||||
|
||||
Rect screen = new Rect(
|
||||
new Point(monitor.Left, monitor.Top),
|
||||
new Point(monitor.Right, monitor.Bottom));
|
||||
Rect wnd = new Rect(
|
||||
new Point(left, top),
|
||||
new Point(left + window.Width, top + window.Height));
|
||||
|
||||
window.Top = wnd.Top;
|
||||
window.Left = wnd.Left;
|
||||
|
||||
if (!screen.Contains(wnd))
|
||||
{
|
||||
if (wnd.Top < screen.Top)
|
||||
{
|
||||
double diff = Math.Abs(screen.Top - wnd.Top);
|
||||
window.Top = wnd.Top + diff;
|
||||
}
|
||||
|
||||
if (wnd.Bottom > screen.Bottom)
|
||||
{
|
||||
double diff = wnd.Bottom - screen.Bottom;
|
||||
window.Top = wnd.Top - diff;
|
||||
}
|
||||
|
||||
if (wnd.Left < screen.Left)
|
||||
{
|
||||
double diff = Math.Abs(screen.Left - wnd.Left);
|
||||
window.Left = wnd.Left + diff;
|
||||
}
|
||||
|
||||
if (wnd.Right > screen.Right)
|
||||
{
|
||||
double diff = wnd.Right - screen.Right;
|
||||
window.Left = wnd.Left - diff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region W32 API
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool GetCursorPos(ref W32Point pt);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool GetMonitorInfo(IntPtr hMonitor, ref W32MonitorInfo lpmi);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern IntPtr MonitorFromPoint(W32Point pt, uint dwFlags);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct W32Point
|
||||
{
|
||||
public int X;
|
||||
public int Y;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct W32MonitorInfo
|
||||
{
|
||||
public int Size;
|
||||
public W32Rect Monitor;
|
||||
public W32Rect WorkArea;
|
||||
public uint Flags;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct W32Rect
|
||||
{
|
||||
public int Left;
|
||||
public int Top;
|
||||
public int Right;
|
||||
public int Bottom;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user