|
|
|
@ -2,50 +2,18 @@ |
|
|
|
using System; |
|
|
|
using System; |
|
|
|
using System.Collections; |
|
|
|
using System.Collections; |
|
|
|
using System.Collections.Generic; |
|
|
|
using System.Collections.Generic; |
|
|
|
using System.Collections.ObjectModel; |
|
|
|
|
|
|
|
using System.ComponentModel; |
|
|
|
using System.ComponentModel; |
|
|
|
using System.Diagnostics; |
|
|
|
|
|
|
|
using System.Runtime.CompilerServices; |
|
|
|
|
|
|
|
using System.Windows; |
|
|
|
using System.Windows; |
|
|
|
using System.Windows.Controls; |
|
|
|
using System.Windows.Controls; |
|
|
|
using System.Windows.Data; |
|
|
|
using System.Windows.Data; |
|
|
|
|
|
|
|
|
|
|
|
namespace MultiTerm.Wpf.CustomControl; |
|
|
|
namespace MultiTerm.Wpf.CustomControl; |
|
|
|
|
|
|
|
|
|
|
|
/// <summary> |
|
|
|
|
|
|
|
/// Follow steps 1a or 1b and then 2 to use this custom control in a XAML file. |
|
|
|
|
|
|
|
/// |
|
|
|
|
|
|
|
/// Step 1a) Using this custom control in a XAML file that exists in the current project. |
|
|
|
|
|
|
|
/// Add this XmlNamespace attribute to the root element of the markup file where it is |
|
|
|
|
|
|
|
/// to be used: |
|
|
|
|
|
|
|
/// |
|
|
|
|
|
|
|
/// xmlns:MyNamespace="clr-namespace:MultiTerm.Wpf.CustomControl.MultiFormatDataView" |
|
|
|
|
|
|
|
/// |
|
|
|
|
|
|
|
/// |
|
|
|
|
|
|
|
/// Step 1b) Using this custom control in a XAML file that exists in a different project. |
|
|
|
|
|
|
|
/// Add this XmlNamespace attribute to the root element of the markup file where it is |
|
|
|
|
|
|
|
/// to be used: |
|
|
|
|
|
|
|
/// |
|
|
|
|
|
|
|
/// xmlns:MyNamespace="clr-namespace:MultiTerm.Wpf.CustomControl.MultiFormatDataView;assembly=MultiTerm.Wpf.CustomControl.MultiFormatDataView" |
|
|
|
|
|
|
|
/// |
|
|
|
|
|
|
|
/// You will also need to add a project reference from the project where the XAML file lives |
|
|
|
|
|
|
|
/// to this project and Rebuild to avoid compilation errors: |
|
|
|
|
|
|
|
/// |
|
|
|
|
|
|
|
/// Right click on the target project in the Solution Explorer and |
|
|
|
|
|
|
|
/// "Add Reference"->"Projects"->[Browse to and select this project] |
|
|
|
|
|
|
|
/// |
|
|
|
|
|
|
|
/// |
|
|
|
|
|
|
|
/// Step 2) |
|
|
|
|
|
|
|
/// Go ahead and use your control in the XAML file. |
|
|
|
|
|
|
|
/// |
|
|
|
|
|
|
|
/// <MyNamespace:MultiFormatDataView/> |
|
|
|
|
|
|
|
/// |
|
|
|
|
|
|
|
/// </summary> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public class MultiFormatDataView : Control |
|
|
|
public class MultiFormatDataView : Control |
|
|
|
{ |
|
|
|
{ |
|
|
|
private static readonly Dictionary<StackPanel, MultiFormatDataView> itemParentPairs = new(); |
|
|
|
private static readonly Dictionary<StackPanel, MultiFormatDataView> itemParentPairs = new(); |
|
|
|
private const string itemsControlTemplateName = "itemsControl"; |
|
|
|
private const string itemsControlTemplateName = "itemsControl"; |
|
|
|
|
|
|
|
private const string buttonClearTemplateName = "btnClear"; |
|
|
|
private ListBox? itemsControl; |
|
|
|
private ListBox? itemsControl; |
|
|
|
private List<DataViewModel> currentSelectedItems = new(); |
|
|
|
private List<DataViewModel> currentSelectedItems = new(); |
|
|
|
|
|
|
|
|
|
|
|
@ -80,6 +48,8 @@ public class MultiFormatDataView : Control |
|
|
|
typeof(MultiFormatDataView), |
|
|
|
typeof(MultiFormatDataView), |
|
|
|
new UIPropertyMetadata(false, OnItemUnloaded)); |
|
|
|
new UIPropertyMetadata(false, OnItemUnloaded)); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static readonly RoutedEvent ClearRequestedEvent; |
|
|
|
|
|
|
|
|
|
|
|
/// <summary> |
|
|
|
/// <summary> |
|
|
|
/// .NET Property for DataSource. |
|
|
|
/// .NET Property for DataSource. |
|
|
|
/// </summary> |
|
|
|
/// </summary> |
|
|
|
@ -127,11 +97,24 @@ public class MultiFormatDataView : Control |
|
|
|
get { return (bool)GetValue(ItemUnloadedProperty); } |
|
|
|
get { return (bool)GetValue(ItemUnloadedProperty); } |
|
|
|
set { SetValue(ItemUnloadedProperty, value); } |
|
|
|
set { SetValue(ItemUnloadedProperty, value); } |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary> |
|
|
|
|
|
|
|
/// .NET Property for <see cref="ClearRequestedEvent"/> |
|
|
|
|
|
|
|
/// </summary> |
|
|
|
|
|
|
|
public event RoutedEventHandler ClearRequested |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
add { this.AddHandler(ClearRequestedEvent, value); } |
|
|
|
|
|
|
|
remove { this.RemoveHandler(ClearRequestedEvent, value); } |
|
|
|
|
|
|
|
} |
|
|
|
#endregion |
|
|
|
#endregion |
|
|
|
|
|
|
|
|
|
|
|
static MultiFormatDataView() |
|
|
|
static MultiFormatDataView() |
|
|
|
{ |
|
|
|
{ |
|
|
|
DefaultStyleKeyProperty.OverrideMetadata(typeof(MultiFormatDataView), new FrameworkPropertyMetadata(typeof(MultiFormatDataView))); |
|
|
|
DefaultStyleKeyProperty.OverrideMetadata(typeof(MultiFormatDataView), new FrameworkPropertyMetadata(typeof(MultiFormatDataView))); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ClearRequestedEvent = EventManager.RegisterRoutedEvent("ClearRequested", |
|
|
|
|
|
|
|
RoutingStrategy.Bubble, typeof(RoutedEventArgs), |
|
|
|
|
|
|
|
typeof(MultiFormatDataView)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public override void OnApplyTemplate() |
|
|
|
public override void OnApplyTemplate() |
|
|
|
@ -149,8 +132,41 @@ public class MultiFormatDataView : Control |
|
|
|
{ |
|
|
|
{ |
|
|
|
throw new Exception($"Implementation fault, {itemsControlTemplateName} not found in template."); |
|
|
|
throw new Exception($"Implementation fault, {itemsControlTemplateName} not found in template."); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// get button from template, ignore if it does not exist |
|
|
|
|
|
|
|
if (GetTemplateChild(buttonClearTemplateName) is Button button) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
button.Click += OnClearButtonClicked; ; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private static void OnDataSourcePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// extract instance and guard null |
|
|
|
|
|
|
|
if (d is not MultiFormatDataView mfdv) { return; } |
|
|
|
|
|
|
|
// extract instance of new Value and guard null |
|
|
|
|
|
|
|
if (e.NewValue is not IEnumerable enumerable) { return; } |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// check if enumerable items are all of correct type |
|
|
|
|
|
|
|
foreach (var item in enumerable) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (item is not DataViewModel) |
|
|
|
|
|
|
|
{ throw new ArgumentException($"{nameof(DataSourceProperty)} must be of type {nameof(DataViewModel)}"); } |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// add group property |
|
|
|
|
|
|
|
CollectionView view = (CollectionView)CollectionViewSource.GetDefaultView(e.NewValue); |
|
|
|
|
|
|
|
PropertyGroupDescription groupDescription = new(nameof(DataViewModel.LineIdentifier)); |
|
|
|
|
|
|
|
view.GroupDescriptions.Add(groupDescription); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void OnClearButtonClicked(object sender, RoutedEventArgs e) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// raise clear requested event |
|
|
|
|
|
|
|
RoutedEventArgs args = new(ClearRequestedEvent); |
|
|
|
|
|
|
|
RaiseEvent(args); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#region Selected Items handling |
|
|
|
private void ItemsControl_SelectionChanged(object sender, SelectionChangedEventArgs e) |
|
|
|
private void ItemsControl_SelectionChanged(object sender, SelectionChangedEventArgs e) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if(this.currentSelectedItems == null) |
|
|
|
if(this.currentSelectedItems == null) |
|
|
|
@ -174,29 +190,9 @@ public class MultiFormatDataView : Control |
|
|
|
{ |
|
|
|
{ |
|
|
|
// NOP |
|
|
|
// NOP |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
#endregion |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#region Realized Item Count |
|
|
|
private static void OnDataSourcePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// extract instance and guard null |
|
|
|
|
|
|
|
if (d is not MultiFormatDataView mfdv) { return; } |
|
|
|
|
|
|
|
// extract instance of new Value and guard null |
|
|
|
|
|
|
|
if (e.NewValue is not IEnumerable enumerable) { return; } |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// check if enumerable items are all of correct type |
|
|
|
|
|
|
|
foreach (var item in enumerable) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (item is not DataViewModel) |
|
|
|
|
|
|
|
{ throw new ArgumentException($"{nameof(DataSourceProperty)} must be of type {nameof(DataViewModel)}"); } |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// add group property |
|
|
|
|
|
|
|
CollectionView view = (CollectionView)CollectionViewSource.GetDefaultView(e.NewValue); |
|
|
|
|
|
|
|
PropertyGroupDescription groupDescription = new(nameof(DataViewModel.LineIdentifier)); |
|
|
|
|
|
|
|
view.GroupDescriptions.Add(groupDescription); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static void OnRealizedItemsCountChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
|
|
private static void OnRealizedItemsCountChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
|
|
|
{ |
|
|
|
{ |
|
|
|
// NOP |
|
|
|
// NOP |
|
|
|
@ -238,4 +234,5 @@ public class MultiFormatDataView : Control |
|
|
|
parentMFDV.RealizedItemsCount--; |
|
|
|
parentMFDV.RealizedItemsCount--; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
#endregion |
|
|
|
} |
|
|
|
} |
|
|
|
|