【问题标题】:WPF/C# entirely programmatically binding an array of objects to a static ObservableCollectionWPF/C# 完全以编程方式将对象数组绑定到静态 ObservableCollection
【发布时间】:2015-10-25 09:20:48
【问题描述】:

请假设整个问题都涉及代码,没有任何 XAML。

我有一个名为myStaticList 的静态ObservableCollection。它是名为myClass 的非静态类的一部分。

public class myClass
{
    public static ObservableCollection<CheckBoxStructure> myStaticList { get; set; }

    static myClass()
    {
        myStaticList = new ObservableCollection<CheckBoxStructure>();
    }
}

以及CheckBoxStructure的定义:

public class CheckBoxStructure
{
    public string Description { get; set; }
    public bool IsSelected { get; set; }
}

此外,还有一个名为 checkBoxArray[] 的复选框数组,包含 3 个元素。每个复选框都有一个文本框作为内容。

我想做的是以编程方式绑定(双向)这两个,这样checkBoxArray[]数组中复选框的IsChecked属性将绑定到@987654331的IsSelected属性@ 的CheckBoxStructure,复选框内容中的文本框文本与myStaticListCheckBoxStructureDescription 属性之间也是如此。

此外,我想避免使用循环,因为如果这两个列表的大小发生变化,最好让它们相互更新。

这怎么可能?

谢谢!

【问题讨论】:

  • @Bahman_Aries:感谢您的编辑。只是打字错误。

标签: c# arrays wpf checkbox binding


【解决方案1】:

使用 XAML,一个简单的方法是为它声明一个 ItemsControl 和一个 DataTemplate,这样你就可以有一个 UserControlCheckBoxTextBox 在里面),它的 DataContextCheckBoxStructure。这样,绑定在CheckBox.IsCheckedIsSelected 属性之间以及TextBox.TextDescription 属性之间工作。 如果您只需要在代码中执行此操作,则必须创建相同的行为(ItemsControlDataTemplate)。您至少有 2 个选项

1.

DataTemplate template = new DataTemplate();
FrameworkElementFactory factory = new FrameworkElementFactory(typeof(StackPanel));
template.VisualTree = factory;
FrameworkElementFactory childFactory = new FrameworkElementFactory(typeof(CheckBox));
childFactory.SetBinding(CheckBox.IsChecked, new Binding("IsSelected"));
factory.AppendChild(childFactory);
childFactory = new FrameworkElementFactory(typeof(TextBox));
childFactory.SetBinding(Label.ContentProperty, new Binding("Description"));
factory.AppendChild(childFactory);

2.

MemoryStream sr = null;
ParserContext pc = null;
string xaml = string.Empty;
xaml = "<DataTemplate><StackPanel><TextBlock Text="{Binding Description"/><CheckBox IsChecked="{Binding IsSelected"/></StackPanel></DataTemplate>";
sr = new MemoryStream(Encoding.ASCII.GetBytes(xaml));
pc = new ParserContext();
pc.XmlnsDictionary.Add("", "http://schemas.microsoft.com/winfx/2006/xaml/presentation");
pc.XmlnsDictionary.Add("x", "http://schemas.microsoft.com/winfx/2006/xaml");
DataTemplate datatemplate = (DataTemplate)XamlReader.Load(sr, pc);
this.Resources.Add("dt", datatemplate);

稍后编辑,经过 cmets 的讨论;此示例仅适用于一种绑定方式,但很容易使其成为两种方式。请注意,这只是一个简单的概念示例,并不完整:您需要修改列表类以适合您希望对象配对的方式,您可能需要为极端情况添加更多保护,您可能需要使其线程安全等等...

首先是基本的绑定对象:

class Binder
{
    public Binder()
    {
        _bindings = new Dictionary<string, List<string>>();
    }

    private INotifyPropertyChanged _dataContext;

    public INotifyPropertyChanged DataContext
    {
        get { return _dataContext; }
        set
        {
            if (_dataContext != null)
            {
                _dataContext.PropertyChanged -= _dataContext_PropertyChanged;
            }

            _dataContext = value;

            _dataContext.PropertyChanged += _dataContext_PropertyChanged;
        }
    }

    void _dataContext_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (_bindings.ContainsKey(e.PropertyName))
        {
            var bindableType = _dataContext.GetType();
            var bindableProp = bindableType.GetProperty(e.PropertyName);
            if (bindableProp == null)
            {
                return;
            }

            var binderType = this.GetType();
            foreach (var binderPropName in _bindings[e.PropertyName])
            {
                var binderProp = binderType.GetProperty(binderPropName);
                if (binderProp == null)
                {
                    continue;
                }

                var value = bindableProp.GetValue(_dataContext);
                binderProp.SetValue(this, value);
            }
        }
    }

    Dictionary<string, List<string>> _bindings;

    public void AddBinding(string binderPropertyName, string bindablePropertyName)
    {
        if (!_bindings.ContainsKey(bindablePropertyName))
        {
            _bindings.Add(bindablePropertyName, new List<string>());
        }
        _bindings[bindablePropertyName].Add(bindablePropertyName);
    }
}

class Bindable : INotifyPropertyChanged
{
    protected void NotifyPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

然后是他们的持有名单:

class BindableList<T> : List<T> where T : Bindable
{
    public event Action<T> ItemAdded;

    public new void Add(T item)
    {
        base.Add(item);
        NotifyItemAdded(item);
    }

    private void NotifyItemAdded(T item)
    {
        if (ItemAdded != null)
        {
            ItemAdded(item);
        }
    }
}

class BinderList<T> : List<T> where T : Binder
{
    public BinderList()
    {
        _bindingRules = new Dictionary<string, string>();
    }

    private BindableList<Bindable> _dataContextList;
    public BindableList<Bindable> DataContextList
    {
        get { return _dataContextList; }
        set
        {
            if (_dataContextList != null)
            {
                _dataContextList.ItemAdded -= _dataContextList_ItemAdded;
            }
            _dataContextList = value;

            _dataContextList.ItemAdded += _dataContextList_ItemAdded;
        }
    }

    void _dataContextList_ItemAdded(Bindable obj)
    {
        foreach (var pair in _bindingRules)
        {
            this[Count-1].AddBinding(pair.Key, pair.Value);
            this[Count - 1].DataContext = obj;
        }
    }

    private Dictionary<string, string> _bindingRules;
    public void AddBindingRule(string binderPropertyName, string bindablePropertyName)
    {
        _bindingRules.Add(binderPropertyName, bindablePropertyName);
    }
}

现在是具有属性的实际类:

class BinderElement : Binder
{
    private string _description;

    public string Description
    {
        get { return _description; }
        set { _description = value; }
    }
}

class BindableElement : Bindable
{
    private string _description;
    public string Description
    {
        get
        {
            return _description;
        }
        set
        {
            _description = value;
            NotifyPropertyChanged("Description");
        }
    }
}

以及使用它们的示例:

static void Main(string[] args)
    {
        var bindableList = new BindableList<Bindable>();

        var binderList = new BinderList<BinderElement>()
        {
            new BinderElement(),
            new BinderElement()
        };

        binderList.DataContextList = bindableList;
        binderList.AddBindingRule("Description", "Description");

        bindableList.Add(new BindableElement());
        bindableList.Add(new BindableElement());

        ((BindableElement)bindableList[1]).Description = "This should arrive in BinderElement Description property";

        Console.WriteLine(binderList[1].Description);

        Console.ReadLine();
    }

【讨论】:

  • 谢谢。我将尝试选项#1。我必须说看起来我的问题不够清楚,或者有一个更简单的解决方案。让我问你这个问题:如果绑定应该在两个数组之间(arrayOne 的属性为 arrayOneProp1 和 arrayOneProp2,arrayTwo 的属性为 arrayTwoProp1 和 arrayTwoProp2) - 没有控件:绑定会更简单吗?
  • 如果您没有控件,那么您不必遵循 WPF 的工作方式。因此,您必须拥有或设计另一个绑定系统。但我不认为处理 WPF 可以让这变得更简单。
  • 如果不涉及控制,您能否提出解决方案?
  • 您想要一个只包含基本 C# 类型的小而简单的解决方案吗?
  • 请。如上所述,两个数组的简单解决方案,双向。
猜你喜欢
  • 2012-02-23
  • 1970-01-01
  • 1970-01-01
  • 2015-11-02
  • 2018-09-03
  • 1970-01-01
  • 1970-01-01
  • 2011-05-08
  • 2011-10-22
相关资源
最近更新 更多