wpf - 如何将复选框列表的选择绑定(bind)到数据库 WPF MVVM EF

标签 wpf mvvm data-binding user-controls checkboxlist

我正在尝试使用 MVVM 在 WPF 中创建一个 UserControl 来创建一个 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>

类(class)
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 加载 Sports 类的整个列表,然后选择 Athlete 可以玩的那些?

最佳答案

我找到了解决我的问题的方法。我能找到它here .它是这样的:

WPF 用户控件.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 用户控件.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);
             }));
    }
}

BaseMultiValue 转换器
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
    }
}

多值转换器
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();
        }
    }
}

然后,在要使用它的任何窗口/页面中,您所要做的就是使用以下代码:
<local:CheckBoxList Height="Auto"
                    SelectedItems="{Binding SelectedItems}"
                    ItemsSource="{Binding ItemsSource}"
                    DisplayPropertyPath="Text"/>

关于wpf - 如何将复选框列表的选择绑定(bind)到数据库 WPF MVVM EF,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51482488/

相关文章:

wpf - 具有相同 X 轴值的 OxyPlot WPF Multiple LinearBarSeries

c# - 如何在两个动态生成的组合框上使用不同的值

c# - 如何使用 XAML 在 WPF 中列出颜色?

mvvm - 使用 MVVM 进行 Windows Mobile 开发

c# - MVVM 可以在 View 中命令控件吗?

c# - 与 CollectionViewSource 绑定(bind)

c# - 使用属性数组绑定(bind)到 DataGridView

wpf - 创建 MVVM 的模型部分

c# - 更改 TextBox 边框颜色的最佳实践?

.net - 取消数据绑定(bind)控件验证事件不会抑制更新数据源的尝试