WPF/C# 完全以编程方式将对象数组绑定到静态 ObservableCollection
WPF/C# entirely programmatically binding an array of objects to a static ObservableCollection
请假设整个问题都涉及代码,没有任何 XAML。
我有一个名为 myStaticList
的静态 ObservableCollection
。它是名为 myClass
.
的非静态 class 的一部分
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
属性 将绑定到 myStaticList
's CheckBoxStructure
, and similarly so between the text of the textboxes inthe checkboxes' 内容的 IsSelected
属性 和 myStaticList
的 CheckBoxStructure
的 Description
属性 ].
此外,我想避免使用循环,因为这两个列表最好在大小发生变化时相互更新。
这怎么可能?
谢谢!
使用XAML,一个简单的方法是为其声明一个ItemsControl
和一个DataTemplate
,这样你就可以有一个UserControl
(CheckBox
和 TextBox
inside) 其 DataContext
是 CheckBoxStructure
。这样,绑定在 CheckBox.IsChecked
和 IsSelected
属性 之间以及 TextBox.Text
和 Description
属性 之间起作用。
如果您只需要在代码中这样做,那么您将不得不创建相同的行为(ItemsControl
和 DataTemplate
)。您至少有 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);
稍后编辑,经过评论讨论;此示例仅适用于一种绑定方式,但很容易使其成为两种方式。请注意,这只是概念的一个简单示例,并不完整:您需要修改列表 类 以适应您希望对象配对的方式,您可能需要为极端情况添加更多守卫,您可能需要使其成为线程安全的等等...
首先是基本绑定对象:
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();
}
请假设整个问题都涉及代码,没有任何 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
属性 将绑定到 myStaticList
's CheckBoxStructure
, and similarly so between the text of the textboxes inthe checkboxes' 内容的 IsSelected
属性 和 myStaticList
的 CheckBoxStructure
的 Description
属性 ].
此外,我想避免使用循环,因为这两个列表最好在大小发生变化时相互更新。
这怎么可能?
谢谢!
使用XAML,一个简单的方法是为其声明一个ItemsControl
和一个DataTemplate
,这样你就可以有一个UserControl
(CheckBox
和 TextBox
inside) 其 DataContext
是 CheckBoxStructure
。这样,绑定在 CheckBox.IsChecked
和 IsSelected
属性 之间以及 TextBox.Text
和 Description
属性 之间起作用。
如果您只需要在代码中这样做,那么您将不得不创建相同的行为(ItemsControl
和 DataTemplate
)。您至少有 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);
稍后编辑,经过评论讨论;此示例仅适用于一种绑定方式,但很容易使其成为两种方式。请注意,这只是概念的一个简单示例,并不完整:您需要修改列表 类 以适应您希望对象配对的方式,您可能需要为极端情况添加更多守卫,您可能需要使其成为线程安全的等等...
首先是基本绑定对象:
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();
}