简体   繁体   中英

WPF/C# entirely programmatically binding an array of objects to a static ObservableCollection

Please assume this entire question deals in code, without any XAML.

I have a static ObservableCollection named myStaticList . It's a part of a non-static class named myClass .

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

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

And the definition of CheckBoxStructure :

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

In addition, there's an array of checkboxes called checkBoxArray[] , holding 3 elements. each checkbox has as content a textbox.

What I want to do is programmatically bind (two-way) these two, in such a manner that the IsChecked property of the checkboxes in the checkBoxArray[] array will bind to the IsSelected property of the myStaticList 's CheckBoxStructure , and similarly so between the text of the textboxes inthe checkboxes' content and the Description property of the myStaticList 's CheckBoxStructure .

In addition, I would like to avoid using loops, since it is preferable that this two lists will update each other if they change in size.

How is this possible?

Thanks!

Using XAML, an easy way would be to the declare an ItemsControl and a DataTemplate for it so that you can have a UserControl ( CheckBox and TextBox inside) with its DataContext being a CheckBoxStructure . This way the bindings work between CheckBox.IsChecked and IsSelected property and between TextBox.Text and Description property. If you need to this only in code then you would have to create same behavior ( ItemsControl with a DataTemplate ). You have at least 2 options

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);

Later edit, after discussion from comments; this example works only one way of binding but is easily to make it two ways. Please note that this is only a trivial example of a concept and is not complete: you need to modify the list classes to suit how you wish for objects to be paired, you may need to add more guards for corner cases, you may need to make it thread safe and so on...

First the basic binding objects:

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;
}

Then the holding lists for them:

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);
    }
}

Now the actual classes with properties:

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");
        }
    }
}

And an example to use them:

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();
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM