Files
SzmediTools/Szmedi.RvKits/Behaviors/SelectedItemsBehavior.cs
2025-09-16 16:06:41 +08:00

194 lines
6.3 KiB
C#

using Microsoft.Xaml.Behaviors;
using System.Collections;
using System.Collections.Specialized;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
namespace Szmedi.RvKits.Behaviors;
//public class MyObjectListBoxSelectionBehavior : ListBoxSelectionBehavior<MyObject>
//{
//}
// <i:Interaction.Behaviors>
// <behaviors:MyObjectListBoxSelectionBehavior BindableSelectedItems = "{Binding SelectedItems}" BindableSelectedValues="{Binding SelectedValues}"/>
// </ i:Interaction.Behaviors>
internal class SelectedItemsBehavior<T> : Behavior<MultiSelector>
{
// Using a DependencyProperty as the backing store for BindableSelectedItems. This enables animation, styling, binding, etc...
public static readonly DependencyProperty BindableSelectedItemsProperty =
DependencyProperty.Register(
nameof(BindableSelectedItems),
typeof(IList),
typeof(SelectedItemsBehavior<T>),
new FrameworkPropertyMetadata(
null,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
OnSelectedItemsChanged));
public static readonly DependencyProperty BindableSelectedValuesProperty =
DependencyProperty.Register(
nameof(BindableSelectedValues),
typeof(IList),
typeof(SelectedItemsBehavior<T>),
new FrameworkPropertyMetadata(
null,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
OnSelectedItemsChanged
)
);
private bool modelHandled;
private bool viewHandled;
public IList BindableSelectedItems
{
get => (IList)GetValue(BindableSelectedItemsProperty);
set => SetValue(BindableSelectedItemsProperty, value);
}
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.SelectionChanged += OnSelectionChanged;
((INotifyCollectionChanged)AssociatedObject.Items).CollectionChanged += OnItemsChanged;
}
protected override void OnDetaching()
{
base.OnDetaching();
if (AssociatedObject != null)
{
AssociatedObject.SelectionChanged -= OnSelectionChanged;
((INotifyCollectionChanged)AssociatedObject.Items).CollectionChanged -= OnItemsChanged;
}
}
// Propagate selected items from view to model
private void SelectItems()
{
viewHandled = true;
AssociatedObject.SelectedItems.Clear();
if (BindableSelectedItems != null)
{
foreach (var item in BindableSelectedItems)
{
AssociatedObject.SelectedItems.Add(item);
}
}
viewHandled = false;
}
private static void OnSelectedItemsChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
var behavior = (SelectedItemsBehavior<T>)sender;
if (behavior.modelHandled)
{
return;
}
if (behavior.AssociatedObject == null)
{
return;
}
behavior.modelHandled = true;
behavior.SelectItems();
behavior.modelHandled = false;
}
// Re-select items when the set of items changes
private void OnItemsChanged(object sender, NotifyCollectionChangedEventArgs args)
{
if (viewHandled)
{
return;
}
if (AssociatedObject.Items.SourceCollection == null)
{
return;
}
SelectItems();
}
private void OnSelectionChanged(object sender, SelectionChangedEventArgs args)
{
if (viewHandled)
{
return;
}
if (AssociatedObject.Items.SourceCollection == null)
{
return;
}
BindableSelectedItems = AssociatedObject.SelectedItems.Cast<T>().ToArray();
}
private static object GetDeepPropertyValue(object obj, string path)
{
if (string.IsNullOrWhiteSpace(path))
{
return obj;
}
while (true)
{
if (path.Contains('.'))
{
string[] split = path.Split('.');
string remainingProperty = path.Substring(path.IndexOf('.') + 1);
obj = obj.GetType().GetProperty(split[0]).GetValue(obj, null);
path = remainingProperty;
continue;
}
return obj.GetType().GetProperty(path).GetValue(obj, null);
}
}
public IList BindableSelectedValues
{
get => (IList)GetValue(BindableSelectedValuesProperty);
set => SetValue(BindableSelectedValuesProperty, value);
}
// Update SelectedItems based on SelectedValues
private void SelectedValuesToItems()
{
BindableSelectedItems = BindableSelectedValues == null
? null
: (IList)AssociatedObject.Items.Cast<T>()
.Where(i => BindableSelectedValues.Contains(GetDeepPropertyValue(i, AssociatedObject.SelectedValuePath)))
.ToArray();
}
// Update SelectedValues based on SelectedItems
private void SelectedItemsToValues()
{
BindableSelectedValues = BindableSelectedItems == null
? null
: (IList)BindableSelectedItems.Cast<T>()
.Select(i => GetDeepPropertyValue(i, AssociatedObject.SelectedValuePath))
.ToArray();
}
//private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
//{
// var selector = (MultiSelector)sender;
// BindableSelectedItems = selector.BindableSelectedItems;
// selector.GetBindingExpression(BindableSelectedItemsProperty)?.UpdateSource();
//}
//public static IList GetBindableSelectedItems(DependencyObject obj)
//{
// return (IList)obj.GetValue(BindableSelectedItemsProperty);
//}
//public static void SetBindableSelectedItems(DependencyObject obj, IList value)
//{
// obj.SetValue(BindableSelectedItemsProperty, value);
//}
//// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
//public static readonly DependencyProperty BindableSelectedItemsProperty =
// DependencyProperty.RegisterAttached("BindableSelectedItems", typeof(IList), typeof(SelectedItemsBehavior), new PropertyMetadata(null));
}