如何将复选框列表的选择绑定到数据库 WPF MVVM EF
How to bind the selection of CheckBox List to database WPF MVVM EF
我正在尝试创建一个 UserControl 以使用 MVVM 在 WPF 中创建一个 CheckBoxList。此外,Entity Framework 用于部署数据。鉴于以下内容:
WPF(用户控件)
<Grid>
<ListBox Name="ListBox" ItemsSource="{Binding TheList}" >
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Sport}"
Tag="{Binding SportsId}"
IsChecked="{Binding IsChecked}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
类
public class Athlete
{
public int AthleteId { get; set; }
public string Name { get; set; }
public ICollection<Sports> Sports { get; set; }
}
public class Sports {
public int SportsId { get; set; }
public string Sport { get; set; }
}
如何让 UserControl 加载整个列表 class 然后 select 运动员可以玩的列表?
这个问题非常宽泛和模糊,但我会尽力解释。您可能需要至少阅读整篇文章两遍。还要阅读 the external link 到结尾 或至少仔细阅读其中的代码。
先看看最终的解决方案:
public class AthleteVM : DependencyObject
{
public int AthleteId { get; set; }
public string Name { get; set; }
private ObservableCollection<SportSelectionVM> _sports = new ObservableCollection<SportSelectionVM>();
public ObservableCollection<SportSelectionVM> Sports { get { return _sports; } }
}
public class SportSelectionVM : DependencyObject
{
public int SportsId { get; set; }
public string Name { get; set; }
private Model.Sport _model;
public SportSelectionVM(Model.Sport model, bool isSelected)
{
_model = model;
SportsId = model.Id;
Name = model.Name;
IsSelected = isSelected;
}
/// <summary>
/// Gets or Sets IsSelected Dependency Property
/// </summary>
public bool IsSelected
{
get { return (bool)GetValue(IsSelectedProperty); }
set { SetValue(IsSelectedProperty, value); }
}
public static readonly DependencyProperty IsSelectedProperty =
DependencyProperty.Register("IsSelected", typeof(bool), typeof(AthleteVM), new PropertyMetadata(false, (d, e) =>
{
// PropertyChangedCallback
var vm = d as SportSelectionVM;
var val = (bool)e.NewValue;
AthleteDataService.UpdateModel(vm._model, val);//database changes here
}));
}
XAML:
<ListBox Name="ListBox" ItemsSource="{Binding Sports}" >
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Name}"
Tag="{Binding SportsId}"
IsChecked="{Binding IsSelected}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
此视图的 DataContext 是 AthleteVM
的一个实例。将所有运动添加到 AthleteVM
中的 Sports
并在必要时设置 IsSelected
。
查看构造函数:public SportSelectionVM(Model.Sport model, bool isSelected)
应该使用类似的策略来创建 AthleteVM
或在其父项中填充 AthleteVM 列表。
英孚和伍伦贡大学:
正如我们所知,这是 MVVM 背后的理念:
[Model] <--- [VM] <--TwoWay Binding--> [View]
当EF加入此模式后,通常建议也遵循UOW模式。
通常 UOW (UnitOfWork) 是一个对象,它负责 一个 数据库事务(我不是说 SQLTransaction),建议总是在里面创建一个 UOW using
声明,以便之后处理。使用这种方法,您应该期望 运行 进入这个问题:不同的 UOW 如何相互交互。哪个答案是:他们没有。
每个 UOW 创建数据库的惰性副本并开始修改它,直到您告诉它丢弃或保存。如果在此过程中创建了另一个 UOW,则它不包含对以前的 UOW 所做的任何更改,除非保存了以前的 UOW。
因此您不必担心 Model,相反,您将专注于 DataService 以获得一些东西 like this.
型号<->虚拟机
考虑到所有这些信息,ViewModel 只是使用 DataService 的实例从数据库中获取数据并将它们放入可绑定属性中,然后可观察集合以维护 TwoWay Binding.
但是 VM 和 Model 没有 TwoWay 关系,这意味着任何变化ViewModel 应该反映在 Model 上,然后 手动.
保存在数据库中
我最喜欢的解决方案是充分利用 DependencyProperty
的 PropertyChangedCallback
特性来告诉 DataService 反映变化:
public int MyProperty
{
get { return (int)GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); }
}
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register("MyProperty", typeof(int), typeof(MyViewModel),
new PropertyMetadata(0, (d,e)=>
{
var vm = d as MyViewModel;
var val = (int)e.NewValue;//check conditions here
vm._model.MyProperty = val;//update model
vm._dataService.Update(vm._model);//update database
}));
在上面的示例中,class MyViewModel
有一个 _model
和 _dataService
的实例。
我找到了问题的解决方案。我能够找到它 here。它是这样的:
WPF UserControl.xaml
<UserControl x:Class="YourNamespace.CheckBoxList"
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:YourNamespace"
mc:Ignorable="d"
x:Name="ThisCheckBoxList"
d:DesignHeight="450" d:DesignWidth="800">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel>
<ItemsControl x:Name="host"
ItemsSource="{Binding ElementName=ThisCheckBoxList, Path=ItemsSource}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:MyCheckBox x:Name="theCheckbox"
DisplayMemberPath="{Binding ElementName=ThisCheckBoxList, Path=DisplayPropertyPath}"
Unchecked="MyCheckBox_Checked"
Checked="MyCheckBox_Checked"
Tag="{Binding Path=.}">
<local:MyCheckBox.IsChecked >
<MultiBinding Mode="OneWay" >
<MultiBinding.Converter>
<local:IsCheckedValueConverter />
</MultiBinding.Converter>
<Binding Path="."></Binding>
<Binding ElementName="ThisCheckBoxList" Path="SelectedItems"></Binding>
<Binding ElementName="ThisCheckBoxList" Path="DisplayPropertyPath"></Binding>
</MultiBinding>
</local:MyCheckBox.IsChecked>
</local:MyCheckBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ScrollViewer>
</UserControl>
WPF UserControl.xaml.cs
using System.Collections;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
namespace Eden
{
/// <summary>
/// Interaction logic for CheckBoxList.xaml
/// </summary>
public partial class CheckBoxList : UserControl
{
public CheckBoxList()
{
InitializeComponent();
}
public object ItemsSource
{
get => GetValue(ItemsSourceProperty);
set => SetValue(ItemsSourceProperty, value);
}
// Using a DependencyProperty as the backing store for ItemSource. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(object), typeof(CheckBoxList),
new UIPropertyMetadata(null, (sender, args) => Debug.WriteLine(args)));
public IList SelectedItems
{
get => (IList)GetValue(SelectedItemsProperty);
set => SetValue(SelectedItemsProperty, value);
}
// Using a DependencyProperty as the backing store for SelectedItems. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SelectedItemsProperty =
DependencyProperty.Register("SelectedItems", typeof(IList), typeof(CheckBoxList),
new UIPropertyMetadata(null, SelectedChanged));
/// <summary>
/// This is called when selected property changed.
/// </summary>
/// <param name="obj"></param>
/// <param name="args"></param>
private static void SelectedChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
if (args.NewValue is INotifyCollectionChanged ncc)
{
ncc.CollectionChanged += (sender, e) =>
{
CheckBoxList thiscontrol = (CheckBoxList)obj;
RebindAllCheckbox(thiscontrol.host);
};
}
}
private static void RebindAllCheckbox(DependencyObject de)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(de); i++)
{
DependencyObject dobj = VisualTreeHelper.GetChild(de, i);
if (dobj is CheckBox cb)
{
var bexpression = BindingOperations.GetMultiBindingExpression(cb, MyCheckBox.IsCheckedProperty);
if (bexpression != null) bexpression.UpdateTarget();
}
RebindAllCheckbox(dobj);
}
}
public string DisplayPropertyPath
{
get => (string)GetValue(DisplayPropertyPathProperty);
set => SetValue(DisplayPropertyPathProperty, value);
}
// Using a DependencyProperty as the backing store for DisplayPropertyPath. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DisplayPropertyPathProperty =
DependencyProperty.Register("DisplayPropertyPath", typeof(string), typeof(CheckBoxList),
new UIPropertyMetadata("", (sender, args) => Debug.WriteLine(args)));
private PropertyInfo mDisplayPropertyPathPropertyInfo;
private void MyCheckBox_Checked(object sender, RoutedEventArgs e)
{
if (SelectedItems == null)
return;
MyCheckBox chb = (MyCheckBox)sender;
object related = chb.Tag;
if (mDisplayPropertyPathPropertyInfo == null)
{
mDisplayPropertyPathPropertyInfo =
related.GetType().GetProperty(
DisplayPropertyPath, BindingFlags.Instance | BindingFlags.Public);
}
object propertyValue;
if (DisplayPropertyPath == ".")
propertyValue = related;
else
propertyValue = mDisplayPropertyPathPropertyInfo.GetValue(related, null);
if (chb.IsChecked == true)
{
if (!SelectedItems.Cast<object>()
.Any(o => propertyValue.Equals(
DisplayPropertyPath == "." ? o : mDisplayPropertyPathPropertyInfo.GetValue(o, null))))
{
SelectedItems.Add(related);
}
}
else
{
object toDeselect = SelectedItems.Cast<object>()
.Where(o => propertyValue.Equals(DisplayPropertyPath == "." ? o : mDisplayPropertyPathPropertyInfo.GetValue(o, null)))
.FirstOrDefault();
if (toDeselect != null)
{
SelectedItems.Remove(toDeselect);
}
}
}
}
public class MyCheckBox : CheckBox
{
public string DisplayMemberPath
{
get => (string)GetValue(DisplayMemberPathProperty);
set => SetValue(DisplayMemberPathProperty, value);
}
// Using a DependencyProperty as the backing store for DisplayMemberPath. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DisplayMemberPathProperty =
DependencyProperty.Register("DisplayMemberPath",
typeof(string),
typeof(MyCheckBox),
new UIPropertyMetadata(string.Empty, (sender, args) =>
{
MyCheckBox item = (MyCheckBox)sender;
Binding contentBinding = new Binding((string)args.NewValue);
item.SetBinding(ContentProperty, contentBinding);
}));
}
}
BaseMultiValueConverter
using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Markup;
namespace Eden
{
/// <summary>
/// A base value converter that allows direct XAML usage
/// </summary>
/// <typeparam name="T">The type of this value converter</typeparam>
public abstract class BaseMultiValueConverter<T> : MarkupExtension, IMultiValueConverter
where T : class, new()
{
#region Private Variables
/// <summary>
/// A single static instance of this value converter
/// </summary>
private static T Coverter = null;
#endregion
#region Markup Extension Methods
/// <summary>
/// Provides a static instance of the value converter
/// </summary>
/// <param name="serviceProvider">The service provider</param>
/// <returns></returns>
public override object ProvideValue(IServiceProvider serviceProvider)
{
return Coverter ?? (Coverter = new T());
}
#endregion
#region Value Converter Methods
/// <summary>
/// The method to convert on type to another
/// </summary>
/// <param name="value"></param>
/// <param name="targetType"></param>
/// <param name="parameter"></param>
/// <param name="culture"></param>
/// <returns></returns>
public abstract object Convert(object[] value, Type targetType, object parameter, CultureInfo culture);
/// <summary>
/// The method to convert a value back to it's source type
/// </summary>
/// <param name="value"></param>
/// <param name="targetType"></param>
/// <param name="parameter"></param>
/// <param name="culture"></param>
/// <returns></returns>
public abstract object[] ConvertBack(object value, Type[] targetType, object parameter, CultureInfo culture);
#endregion
}
}
IMultiValueConverter
using EcoDev.Data;
using System;
using System.Collections;
using System.Globalization;
using System.Reflection;
using System.Windows.Data;
namespace Eden
{
/// <summary>
///
/// </summary>
public class IsCheckedValueConverter : BaseMultiValueConverter<IsCheckedValueConverter>
{
private PropertyInfo PropertyInfo { get; set; }
private Type ObjectType { get; set; }
public override object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (values[1] == null) return false; // IF I do not have no value for selected simply return false
if (!(values[2] is string PropertyName)) return false;
if (string.IsNullOrEmpty(PropertyName)) return false;
if (!targetType.IsAssignableFrom(typeof(bool))) throw new NotSupportedException("Can convert only to boolean");
IEnumerable collection = values[1] as IEnumerable;
object value = values[0];
if (value.GetType() != ObjectType)
{
PropertyInfo = value.GetType().GetProperty(PropertyName, BindingFlags.Instance | BindingFlags.Public);
ObjectType = value.GetType();
}
foreach (var obj in collection)
{
if (PropertyName == ".")
{
if (value.Equals(obj)) return true;
}
else
{
if (PropertyInfo.GetValue(value, null).Equals(PropertyInfo.GetValue(obj, null))) return true;
}
}
return false;
}
public override object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
然后你需要做的就是 window/page 你想使用它的地方就是使用这个代码:
<local:CheckBoxList Height="Auto"
SelectedItems="{Binding SelectedItems}"
ItemsSource="{Binding ItemsSource}"
DisplayPropertyPath="Text"/>
我正在尝试创建一个 UserControl 以使用 MVVM 在 WPF 中创建一个 CheckBoxList。此外,Entity Framework 用于部署数据。鉴于以下内容:
WPF(用户控件)
<Grid>
<ListBox Name="ListBox" ItemsSource="{Binding TheList}" >
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Sport}"
Tag="{Binding SportsId}"
IsChecked="{Binding IsChecked}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
类
public class Athlete
{
public int AthleteId { get; set; }
public string Name { get; set; }
public ICollection<Sports> Sports { get; set; }
}
public class Sports {
public int SportsId { get; set; }
public string Sport { get; set; }
}
如何让 UserControl 加载整个列表 class 然后 select 运动员可以玩的列表?
这个问题非常宽泛和模糊,但我会尽力解释。您可能需要至少阅读整篇文章两遍。还要阅读 the external link 到结尾 或至少仔细阅读其中的代码。
先看看最终的解决方案:
public class AthleteVM : DependencyObject
{
public int AthleteId { get; set; }
public string Name { get; set; }
private ObservableCollection<SportSelectionVM> _sports = new ObservableCollection<SportSelectionVM>();
public ObservableCollection<SportSelectionVM> Sports { get { return _sports; } }
}
public class SportSelectionVM : DependencyObject
{
public int SportsId { get; set; }
public string Name { get; set; }
private Model.Sport _model;
public SportSelectionVM(Model.Sport model, bool isSelected)
{
_model = model;
SportsId = model.Id;
Name = model.Name;
IsSelected = isSelected;
}
/// <summary>
/// Gets or Sets IsSelected Dependency Property
/// </summary>
public bool IsSelected
{
get { return (bool)GetValue(IsSelectedProperty); }
set { SetValue(IsSelectedProperty, value); }
}
public static readonly DependencyProperty IsSelectedProperty =
DependencyProperty.Register("IsSelected", typeof(bool), typeof(AthleteVM), new PropertyMetadata(false, (d, e) =>
{
// PropertyChangedCallback
var vm = d as SportSelectionVM;
var val = (bool)e.NewValue;
AthleteDataService.UpdateModel(vm._model, val);//database changes here
}));
}
XAML:
<ListBox Name="ListBox" ItemsSource="{Binding Sports}" >
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Name}"
Tag="{Binding SportsId}"
IsChecked="{Binding IsSelected}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
此视图的 DataContext 是 AthleteVM
的一个实例。将所有运动添加到 AthleteVM
中的 Sports
并在必要时设置 IsSelected
。
查看构造函数:public SportSelectionVM(Model.Sport model, bool isSelected)
应该使用类似的策略来创建 AthleteVM
或在其父项中填充 AthleteVM 列表。
英孚和伍伦贡大学:
正如我们所知,这是 MVVM 背后的理念:
[Model] <--- [VM] <--TwoWay Binding--> [View]
当EF加入此模式后,通常建议也遵循UOW模式。
通常 UOW (UnitOfWork) 是一个对象,它负责 一个 数据库事务(我不是说 SQLTransaction),建议总是在里面创建一个 UOW using
声明,以便之后处理。使用这种方法,您应该期望 运行 进入这个问题:不同的 UOW 如何相互交互。哪个答案是:他们没有。
每个 UOW 创建数据库的惰性副本并开始修改它,直到您告诉它丢弃或保存。如果在此过程中创建了另一个 UOW,则它不包含对以前的 UOW 所做的任何更改,除非保存了以前的 UOW。
因此您不必担心 Model,相反,您将专注于 DataService 以获得一些东西 like this.
型号<->虚拟机
考虑到所有这些信息,ViewModel 只是使用 DataService 的实例从数据库中获取数据并将它们放入可绑定属性中,然后可观察集合以维护 TwoWay Binding.
但是 VM 和 Model 没有 TwoWay 关系,这意味着任何变化ViewModel 应该反映在 Model 上,然后 手动.
保存在数据库中我最喜欢的解决方案是充分利用 DependencyProperty
的 PropertyChangedCallback
特性来告诉 DataService 反映变化:
public int MyProperty
{
get { return (int)GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); }
}
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register("MyProperty", typeof(int), typeof(MyViewModel),
new PropertyMetadata(0, (d,e)=>
{
var vm = d as MyViewModel;
var val = (int)e.NewValue;//check conditions here
vm._model.MyProperty = val;//update model
vm._dataService.Update(vm._model);//update database
}));
在上面的示例中,class MyViewModel
有一个 _model
和 _dataService
的实例。
我找到了问题的解决方案。我能够找到它 here。它是这样的:
WPF UserControl.xaml
<UserControl x:Class="YourNamespace.CheckBoxList"
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:YourNamespace"
mc:Ignorable="d"
x:Name="ThisCheckBoxList"
d:DesignHeight="450" d:DesignWidth="800">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel>
<ItemsControl x:Name="host"
ItemsSource="{Binding ElementName=ThisCheckBoxList, Path=ItemsSource}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:MyCheckBox x:Name="theCheckbox"
DisplayMemberPath="{Binding ElementName=ThisCheckBoxList, Path=DisplayPropertyPath}"
Unchecked="MyCheckBox_Checked"
Checked="MyCheckBox_Checked"
Tag="{Binding Path=.}">
<local:MyCheckBox.IsChecked >
<MultiBinding Mode="OneWay" >
<MultiBinding.Converter>
<local:IsCheckedValueConverter />
</MultiBinding.Converter>
<Binding Path="."></Binding>
<Binding ElementName="ThisCheckBoxList" Path="SelectedItems"></Binding>
<Binding ElementName="ThisCheckBoxList" Path="DisplayPropertyPath"></Binding>
</MultiBinding>
</local:MyCheckBox.IsChecked>
</local:MyCheckBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ScrollViewer>
</UserControl>
WPF UserControl.xaml.cs
using System.Collections;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
namespace Eden
{
/// <summary>
/// Interaction logic for CheckBoxList.xaml
/// </summary>
public partial class CheckBoxList : UserControl
{
public CheckBoxList()
{
InitializeComponent();
}
public object ItemsSource
{
get => GetValue(ItemsSourceProperty);
set => SetValue(ItemsSourceProperty, value);
}
// Using a DependencyProperty as the backing store for ItemSource. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(object), typeof(CheckBoxList),
new UIPropertyMetadata(null, (sender, args) => Debug.WriteLine(args)));
public IList SelectedItems
{
get => (IList)GetValue(SelectedItemsProperty);
set => SetValue(SelectedItemsProperty, value);
}
// Using a DependencyProperty as the backing store for SelectedItems. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SelectedItemsProperty =
DependencyProperty.Register("SelectedItems", typeof(IList), typeof(CheckBoxList),
new UIPropertyMetadata(null, SelectedChanged));
/// <summary>
/// This is called when selected property changed.
/// </summary>
/// <param name="obj"></param>
/// <param name="args"></param>
private static void SelectedChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
if (args.NewValue is INotifyCollectionChanged ncc)
{
ncc.CollectionChanged += (sender, e) =>
{
CheckBoxList thiscontrol = (CheckBoxList)obj;
RebindAllCheckbox(thiscontrol.host);
};
}
}
private static void RebindAllCheckbox(DependencyObject de)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(de); i++)
{
DependencyObject dobj = VisualTreeHelper.GetChild(de, i);
if (dobj is CheckBox cb)
{
var bexpression = BindingOperations.GetMultiBindingExpression(cb, MyCheckBox.IsCheckedProperty);
if (bexpression != null) bexpression.UpdateTarget();
}
RebindAllCheckbox(dobj);
}
}
public string DisplayPropertyPath
{
get => (string)GetValue(DisplayPropertyPathProperty);
set => SetValue(DisplayPropertyPathProperty, value);
}
// Using a DependencyProperty as the backing store for DisplayPropertyPath. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DisplayPropertyPathProperty =
DependencyProperty.Register("DisplayPropertyPath", typeof(string), typeof(CheckBoxList),
new UIPropertyMetadata("", (sender, args) => Debug.WriteLine(args)));
private PropertyInfo mDisplayPropertyPathPropertyInfo;
private void MyCheckBox_Checked(object sender, RoutedEventArgs e)
{
if (SelectedItems == null)
return;
MyCheckBox chb = (MyCheckBox)sender;
object related = chb.Tag;
if (mDisplayPropertyPathPropertyInfo == null)
{
mDisplayPropertyPathPropertyInfo =
related.GetType().GetProperty(
DisplayPropertyPath, BindingFlags.Instance | BindingFlags.Public);
}
object propertyValue;
if (DisplayPropertyPath == ".")
propertyValue = related;
else
propertyValue = mDisplayPropertyPathPropertyInfo.GetValue(related, null);
if (chb.IsChecked == true)
{
if (!SelectedItems.Cast<object>()
.Any(o => propertyValue.Equals(
DisplayPropertyPath == "." ? o : mDisplayPropertyPathPropertyInfo.GetValue(o, null))))
{
SelectedItems.Add(related);
}
}
else
{
object toDeselect = SelectedItems.Cast<object>()
.Where(o => propertyValue.Equals(DisplayPropertyPath == "." ? o : mDisplayPropertyPathPropertyInfo.GetValue(o, null)))
.FirstOrDefault();
if (toDeselect != null)
{
SelectedItems.Remove(toDeselect);
}
}
}
}
public class MyCheckBox : CheckBox
{
public string DisplayMemberPath
{
get => (string)GetValue(DisplayMemberPathProperty);
set => SetValue(DisplayMemberPathProperty, value);
}
// Using a DependencyProperty as the backing store for DisplayMemberPath. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DisplayMemberPathProperty =
DependencyProperty.Register("DisplayMemberPath",
typeof(string),
typeof(MyCheckBox),
new UIPropertyMetadata(string.Empty, (sender, args) =>
{
MyCheckBox item = (MyCheckBox)sender;
Binding contentBinding = new Binding((string)args.NewValue);
item.SetBinding(ContentProperty, contentBinding);
}));
}
}
BaseMultiValueConverter
using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Markup;
namespace Eden
{
/// <summary>
/// A base value converter that allows direct XAML usage
/// </summary>
/// <typeparam name="T">The type of this value converter</typeparam>
public abstract class BaseMultiValueConverter<T> : MarkupExtension, IMultiValueConverter
where T : class, new()
{
#region Private Variables
/// <summary>
/// A single static instance of this value converter
/// </summary>
private static T Coverter = null;
#endregion
#region Markup Extension Methods
/// <summary>
/// Provides a static instance of the value converter
/// </summary>
/// <param name="serviceProvider">The service provider</param>
/// <returns></returns>
public override object ProvideValue(IServiceProvider serviceProvider)
{
return Coverter ?? (Coverter = new T());
}
#endregion
#region Value Converter Methods
/// <summary>
/// The method to convert on type to another
/// </summary>
/// <param name="value"></param>
/// <param name="targetType"></param>
/// <param name="parameter"></param>
/// <param name="culture"></param>
/// <returns></returns>
public abstract object Convert(object[] value, Type targetType, object parameter, CultureInfo culture);
/// <summary>
/// The method to convert a value back to it's source type
/// </summary>
/// <param name="value"></param>
/// <param name="targetType"></param>
/// <param name="parameter"></param>
/// <param name="culture"></param>
/// <returns></returns>
public abstract object[] ConvertBack(object value, Type[] targetType, object parameter, CultureInfo culture);
#endregion
}
}
IMultiValueConverter
using EcoDev.Data;
using System;
using System.Collections;
using System.Globalization;
using System.Reflection;
using System.Windows.Data;
namespace Eden
{
/// <summary>
///
/// </summary>
public class IsCheckedValueConverter : BaseMultiValueConverter<IsCheckedValueConverter>
{
private PropertyInfo PropertyInfo { get; set; }
private Type ObjectType { get; set; }
public override object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (values[1] == null) return false; // IF I do not have no value for selected simply return false
if (!(values[2] is string PropertyName)) return false;
if (string.IsNullOrEmpty(PropertyName)) return false;
if (!targetType.IsAssignableFrom(typeof(bool))) throw new NotSupportedException("Can convert only to boolean");
IEnumerable collection = values[1] as IEnumerable;
object value = values[0];
if (value.GetType() != ObjectType)
{
PropertyInfo = value.GetType().GetProperty(PropertyName, BindingFlags.Instance | BindingFlags.Public);
ObjectType = value.GetType();
}
foreach (var obj in collection)
{
if (PropertyName == ".")
{
if (value.Equals(obj)) return true;
}
else
{
if (PropertyInfo.GetValue(value, null).Equals(PropertyInfo.GetValue(obj, null))) return true;
}
}
return false;
}
public override object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
然后你需要做的就是 window/page 你想使用它的地方就是使用这个代码:
<local:CheckBoxList Height="Auto"
SelectedItems="{Binding SelectedItems}"
ItemsSource="{Binding ItemsSource}"
DisplayPropertyPath="Text"/>