如何在两种方式绑定 WPF 自定义控件 C# 中通过依赖项 属性 return 值?

How to return value via Dependency Property in two Way Binding WPF Custom Control C#?

我有一个自定义控件,它继承了 ListBox 控件,因为我添加了两个 DependencyProperty,一个用于获取输入,另一个用于通过绑定发送处理后的输出。

下面是 ListBox 继承的自定义控件 BListBox :- C# 编码

public class BListBox : ListBox
{
    static BListBox()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(BListBox), new FrameworkPropertyMetadata(typeof(ListBox)));
    }

    public static readonly DependencyProperty FilterKeyProperty =
    DependencyProperty.Register("FilterKey", typeof(string), typeof(BListBox), new UIPropertyMetadata(null));

    public string FilterKey
    {
        get { return (string)GetValue(FilterKeyProperty); }
        set { SetValue(FilterKeyProperty, value); }
    }

    public static readonly DependencyProperty FilterDictionaryProperty =
        DependencyProperty.Register("FilterDictionary", typeof(Dictionary<string, ObservableCollection<CheckedListItem<string>>>), typeof(BListBox), new UIPropertyMetadata(null));

    public Dictionary<string, ObservableCollection<CheckedListItem<string>>> FilterDictionary
    {
        get { return (Dictionary<string, ObservableCollection<CheckedListItem<string>>>)GetValue(FilterDictionaryProperty); }
        set { SetValue(FilterDictionaryProperty, value); }
    }

    public static readonly DependencyProperty InputSourceProperty =
    DependencyProperty.Register("InputSource", typeof(IEnumerable), typeof(BListBox), new UIPropertyMetadata(null));

    public IEnumerable InputSource
    {
        get { return (IEnumerable)GetValue(InputSourceProperty); }
        set { SetValue(InputSourceProperty, value); }
    }

    public static readonly DependencyProperty OutputSourceProperty = DependencyProperty.Register("OutputSource",
                                                                                                        typeof(IEnumerable),
                                                                                                        typeof(BListBox),
                                                                                                        new FrameworkPropertyMetadata(
                                                                                                            null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

    public IEnumerable OutputSource
    {
        get { return (IEnumerable)GetValue(OutputSourceProperty); }
        set { SetValue(OutputSourceProperty, value); }
    }

    ObservableCollection<dynamic> InputSourceCollection = new ObservableCollection<dynamic>();
    ObservableCollection<dynamic> OutputSourceCollection = new ObservableCollection<dynamic>();
    ObservableCollection<dynamic> SourceCollection = new ObservableCollection<dynamic>();

    public void FilterResult(CheckedListItem<string> curFilter)
    {
       ObservableCollection<CheckedListItem<string>> CustomerFilters = ItemsSource != null ? ItemsSource as ObservableCollection<CheckedListItem<string>> : new ObservableCollection<CheckedListItem<string>>();

        if (CustomerFilters.Count > 0)
        {
            if (InputSourceCollection.Count ==0)
                InputSourceCollection = IEnumeratorToObservableCollection(InputSource);

            OutputSourceCollection = IEnumeratorToObservableCollection(OutputSource);

            if (FilterDictionary.Remove(FilterKey))
                FilterDictionary.Add(FilterKey, new ObservableCollection<CheckedListItem<string>>(CustomerFilters));

            RelationBetweenFiltersCheck(curFilter.Item.ToString());

            List<string> _filterValueList = new List<string>();
            foreach (var item in (FilterDictionary[curFilter.Key.ToString()] as ObservableCollection<CheckedListItem<string>>).Where(x => x.IsChecked == true))
            {
                _filterValueList.Add(item.Item);
            }

            List<string> keyList = new List<string>(FilterDictionary.Keys);

            SourceCollection = new ObservableCollection<dynamic>();

            foreach (var key in keyList)
            {
                foreach (var item in InputSourceCollection)
                {
                    if (item.GetType().GetProperty(key) != null)
                    {
                        if (_filterValueList.Contains(item.GetType().GetProperty(key).GetValue(item, null).ToString()))
                        {
                            SourceCollection.Add(item);
                        }
                    }
                }
            }

            OutputSource = SourceCollection;

            RelationBetweenFiltersCheck();
        }
    }

    private void RelationBetweenFiltersCheck(string unCheckKey = null)
    {
        List<string> fileterKey = new List<string>();

        List<string> keyList = new List<string>(FilterDictionary.Keys);

        foreach (var kItem in keyList)
        {
            if (fileterKey.Count == 0)
            {
                foreach (var item in OutputSourceCollection)
                {
                    if (item.GetType().GetProperty(kItem) != null)
                        fileterKey.Add(item.GetType().GetProperty(kItem).GetValue(item, null).ToString());
                }
            }
            else if (fileterKey.Count > 0)
            {
                foreach (var item in OutputSourceCollection)
                {
                    if (item.GetType().GetProperty(kItem) != null)
                        fileterKey.Add(item.GetType().GetProperty(kItem).GetValue(item, null).ToString());
                }
            }
        }

        if (!string.IsNullOrEmpty(unCheckKey))
        {
            if (fileterKey.Any(s => unCheckKey.Contains(s)))
                fileterKey.Remove(unCheckKey);
            else
                fileterKey.Add(unCheckKey);
        }

        foreach (var item in FilterDictionary)
        {
            ObservableCollection<CheckedListItem<string>> cList = new ObservableCollection<CheckedListItem<string>>();
            cList = item.Value;
            foreach (var sItem in cList)
            {
                sItem.IsChecked = fileterKey.Any(s => sItem.Item.Contains(s));
            }
        }
    }


    private ObservableCollection<dynamic> IEnumeratorToObservableCollection(IEnumerable source)
    {

        ObservableCollection<dynamic> SourceCollection = new ObservableCollection<dynamic>();

        IEnumerator enumItem = source.GetEnumerator();
        var gType = source.GetType();
        string collectionFullName = gType.FullName;
        Type[] genericTypes = gType.GetGenericArguments();
        string className = genericTypes[0].Name;
        string classFullName = genericTypes[0].FullName;
        string assName = (classFullName.Split('.'))[0];

        // Get the type contained in the name string
        Type type = Type.GetType(classFullName, true);

        // create an instance of that type
        object instance = Activator.CreateInstance(type);

        /// List of Propery for the above created instance of a dynamic class
        List<PropertyInfo> oProperty = instance.GetType().GetProperties().ToList();

        while (enumItem.MoveNext())
        {
            Object instanceInner = Activator.CreateInstance(type);
            var x = enumItem.Current;

            foreach (var item in oProperty)
            {
                if (x.GetType().GetProperty(item.Name) != null)
                {
                    var propertyValue = x.GetType().GetProperty(item.Name).GetValue(x, null);
                    if (propertyValue != null)
                    {
                        // Get a property on the type that is stored in the 
                        // property string
                        PropertyInfo prop = type.GetProperty(item.Name);

                        // Set the value of the given property on the given instance
                        prop.SetValue(instanceInner, propertyValue, null);
                    }
                }
            }

            SourceCollection.Add(instanceInner);
        }

        return SourceCollection;
    }

}

XAML代码:

<cust:BListBox ItemsSource="{Binding CustomerFilters}" BorderThickness="0" FilterDictionary="{Binding dictionary, Mode= TwoWay, UpdateSourceTrigger=PropertyChanged}" FilterKey="{Binding FilterKey}" InputSource="{Binding MobileListPrimary}" OutputSource="{Binding MobileList, Mode=TwoWay}">
<cust:BListBox.ItemTemplate>
    <DataTemplate>
        <CheckBox IsChecked="{Binding IsChecked}" Content="{Binding Item}" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.FilterDataGrid}">
            <CheckBox.CommandParameter>
                <MultiBinding Converter="{StaticResource DataGridConverterKey}">
                    <Binding RelativeSource="{ RelativeSource
                                            Mode=FindAncestor,
                                            AncestorType={x:Type ListBox}}" />
                    <Binding />
                </MultiBinding>
            </CheckBox.CommandParameter>
        </CheckBox>
    </DataTemplate>
</cust:BListBox.ItemTemplate>

如何在方法

中将值设置为 OutputSource
FilterResult(CheckedListItem<string> curFilter)
{
/////////////
// Refer the Above C# Code

OutputSource = SourceCollection;

// Refer the Above C# Code
////////////
}

型号:-

public class MobileModel : Notify
{
    private string _brand = string.Empty;
    private ObservableCollection<MobileModelInfo> _model = new ObservableCollection<MobileModelInfo>();
    private string _os = string.Empty;

    public string Brand
    {
        get { return _brand; }
        set { _brand = value; OnPropertyChanged(); }
    }
    public ObservableCollection<MobileModelInfo> Model
    {
        get { return _model; }
        set { _model = value; OnPropertyChanged(); }
    }

    public string OS
    {
        get { return _os; }
        set { _os = value; OnPropertyChanged(); }
    }
}

public class MobileModelInfo
{
    public string Name { get; set; }
    public string Catagory { get; set; }
    public string Year { get; set; }
}

在视图模型中加载模型:-

public void GetMobile()
    {
        List<MobileModel> mList = new List<MobileModel>();
        List<MobileModelInfo> modList = new List<MobileModelInfo>();
        MobileModel mob = new MobileModel();

        modList.Clear();
        mob.Brand = "Apple";
        modList.Add(new MobileModelInfo { Name = "iPhone 4", Catagory = "Smart Phone", Year = "2011" });
        modList.Add(new MobileModelInfo { Name = "iPhone 5", Catagory = "Smart Phone", Year = "2013" });
        modList.Add(new MobileModelInfo { Name = "iPhone 6", Catagory = "Premium Smart Phone", Year = "2015" });
        mob.Model = new ObservableCollection<MobileModelInfo>(modList);
        mob.OS = "IOS";
        mList.Add(mob);

        mob = new MobileModel();
        modList.Clear();
        mob.Brand = "Samsung";
        modList.Add(new MobileModelInfo { Name = "S4", Catagory = "Smart Phone", Year = "2011" });
        modList.Add(new MobileModelInfo { Name = "S5", Catagory = "Smart Phone", Year = "2013" });
        modList.Add(new MobileModelInfo { Name = "S6", Catagory = "Ultra Smart Phone", Year = "2015" });
        mob.Model = new ObservableCollection<MobileModelInfo>(modList);
        mob.OS = "Android";
        mList.Add(mob);

        mob = new MobileModel();
        modList.Clear();
        mob.Brand = "MicroSoft";
        modList.Add(new MobileModelInfo { Name = "Lumina 9900", Catagory = "Phone", Year = "2011" });
        modList.Add(new MobileModelInfo { Name = "Opera X220", Catagory = "Smart Phone", Year = "2013" });
        mob.Model = new ObservableCollection<MobileModelInfo>(modList);
        mob.OS = "Windows";
        mList.Add(mob);

        mob = new MobileModel();
        modList.Clear();
        mob.Brand = "Sony Ericssion";
        modList.Add(new MobileModelInfo { Name = "S4", Catagory = "Smart Phone", Year = "2011" });
        modList.Add(new MobileModelInfo { Name = "S5", Catagory = "Smart Phone", Year = "2013" });
        modList.Add(new MobileModelInfo { Name = "S6", Catagory = "Ultra Smart Phone", Year = "2015" });
        mob.Model = new ObservableCollection<MobileModelInfo>(modList);
        mob.OS = "Android";
        mList.Add(mob);

        MobileListPrimary = new ObservableCollection<MobileModel>(mList);
        MobileList = new ObservableCollection<MobileModel>(MobileListPrimary);
    }

问题在于 XAML 绑定不是查看实际数据类型,而是查看依赖项 属性 的数据类型,即 IEnumerable。该类型的变量无法分配给 ObservableCollection<T>,因此您模型上的值不会更新。

您可以通过添加转换器并在绑定中使用它来解决此问题:

public class EnumerableConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value == null)
            return null;

        if (targetType.IsInstanceOfType(value))
            return value;

        if (value.GetType().GetInterfaces().Contains(typeof(IEnumerable)) && targetType.IsGenericType
            && !targetType.IsGenericTypeDefinition && targetType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
            return ((IEnumerable)value).Cast(targetType.GetGenericArguments().Single());

        throw new InvalidOperationException(
            string.Format("Can't convert from {0} to {1}", value.GetType(), targetType));
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return Convert(value, targetType, parameter, culture);
    }
}

此代码使用 Cast 的非通用版本,因此您还需要此 class:

public static class EnumerableExtensions
{
    private static readonly MethodInfo CastMethod;

    static EnumerableExtensions()
    {
        CastMethod = typeof(Enumerable).GetMethod("Cast", BindingFlags.Public | BindingFlags.Static);
    }

    public static object Cast(this IEnumerable input, Type targetType)
    {
        return CastMethod.MakeGenericMethod(targetType).Invoke(null, new object[] { input });
    }
}

而不是使用 ObservableCollection<T> 使用 IEnumerable

依赖属性声明应该是

public static readonly DependencyProperty OutputSourceCollectionProperty 
    = DependencyProperty.Register("OutputSourceCollection",
        typeof(IEnumerable), 
        typeof(BListBox), 
        new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

public IEnumerable OutputSourceCollection
{
    get { return (IEnumerable)GetValue(OutputSourceCollectionProperty); }
    set { SetValue(OutputSourceCollectionProperty, value); }
}

使用 IList 界面到 Add/Remove 个项目

(OutputSourceCollection as IList).Add(Item);

(OutputSourceCollection as IList).Remove(Item);