WP&Win8中的多值绑定,
WP&Win8中的多值绑定,
在项目开发过程中会遇到用多个属性决定控件状态的情况,这时候就需要用到多值绑定了。Wp&Win8并不提供这个功能,我模仿Wpf中的功能实现了Wp&Win8下的替代方案。
首先阐述下实现原理:
1 通过反射获取目标控件上指定的属性;
2 用一个绑定列表指定数据源上需要绑定的属性;
3 用一个转换器将绑定的数据列表转换成需要的状态并赋值给控件上指定的属性;
然后就是实现代码了,由于代码较多,就不一一贴出来了,只贴关键代码,剩下的各位看示例吧。
下面是通过反射获取目标属性:[csharp] void ParseTargetProperty()
{
TargetDependencyProperty = null;
if (string.IsNullOrEmpty(TargetProperty) || AssociatedObject == null)
return;
var sourceDependencyPropertyField = AssociatedObject.
GetType().GetField(TargetProperty + "Property");
if (sourceDependencyPropertyField != null)
{
TargetDependencyProperty = sourceDependencyPropertyField.
GetValue(null) as DependencyProperty;//获取依赖属性
}
TargetPropertyInfo = AssociatedObject.GetType().GetProperty(TargetProperty);//获取属性的反射器
if (TargetPropertyInfo != null)
TargetPropertyType = TargetPropertyInfo.PropertyType;
}
void ParseTargetProperty()
{
TargetDependencyProperty = null;
if (string.IsNullOrEmpty(TargetProperty) || AssociatedObject == null)
return;
var sourceDependencyPropertyField = AssociatedObject.
GetType().GetField(TargetProperty + "Property");
if (sourceDependencyPropertyField != null)
{
TargetDependencyProperty = sourceDependencyPropertyField.
GetValue(null) as DependencyProperty;//获取依赖属性
}
TargetPropertyInfo = AssociatedObject.GetType().GetProperty(TargetProperty);//获取属性的反射器
if (TargetPropertyInfo != null)
TargetPropertyType = TargetPropertyInfo.PropertyType;
} 由于控件上的属性大多是依赖属性,直接通过依赖属性赋值会快的多。各别非依赖属性则只能依靠反射器赋值了。这里
还获取了目标属性的类型,作为转换器传递的参数使用。
绑定列表的实现则稍显麻烦了。下面是绑定器的代码:[csharp] public class BindingSource : DependencyObject
{
#region Value
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(object), typeof(BindingSource),
new PropertyMetadata(null, OnValueChanged));
public object Value
{
get { return GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
private static void OnValueChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
{
BindingSource slave = depObj as BindingSource;
if (slave != null)
slave.OnValueChanged();
}
#endregion
#region Binding Property
public bool BindsDirectlyToSource { get; set; }
public IValueConverter Converter { get; set; }
public CultureInfo ConverterCulture { get; set; }
public object ConverterParameter { get; set; }
public string ElementName { get; set; }
public BindingMode Mode { get; set; }
public bool NotifyOnValidationError { get; set; }
public string Path { get; set; }
public RelativeSource RelativeSource { get; set; }
public object Source { get; set; }
public UpdateSourceTrigger UpdateSourceTrigger { get; set; }
public bool ValidatesOnDataErrors { get; set; }
public bool ValidatesOnExceptions { get; set; }
public bool ValidatesOnNotifyDataErrors { get; set; }
#endregion
Binding _binding;
public event Action<object, object> ValueChanged;
protected void OnValueChanged()
{
var hander = this.ValueChanged;
if (hander != null)
{
hander(this, Value);
}
}
public void Init()
{
_binding = new Binding();
_binding.BindsDirectlyToSource = this.BindsDirectlyToSource;
_binding.Converter = this.Converter;
_binding.ConverterCulture = this.ConverterCulture;
_binding.ConverterParameter = this.ConverterParameter;
if (!string.IsNullOrEmpty(this.ElementName))
{
_binding.ElementName = this.ElementName;
}
_binding.Mode = this.Mode;
_binding.NotifyOnValidationError = this.NotifyOnValidationError;
_binding.Path = new PropertyPath(this.Path);
if (string.IsNullOrEmpty(_binding.ElementName) && this.RelativeSource != null)
{
_binding.RelativeSource = this.RelativeSource;
}
if (string.IsNullOrEmpty(_binding.ElementName) && this.RelativeSource == null)
{
_binding.Source = this.Source;
}
_binding.UpdateSourceTrigger = this.UpdateSourceTrigger;
_binding.ValidatesOnDataErrors = this.ValidatesOnDataErrors;
_binding.ValidatesOnExceptions = this.ValidatesOnExceptions;
_binding.ValidatesOnNotifyDataErrors = this.ValidatesOnNotifyDataErrors;
BindingOperations.SetBinding(this, ValueProperty, _binding);
}
}
public class BindingSource : DependencyObject
{
#region Value
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(object), typeof(BindingSource),
new PropertyMetadata(null, OnValueChanged));
public object Value
{
get { return GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
private static void OnValueChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
{
BindingSource slave = depObj as BindingSource;
if (slave != null)
slave.OnValueChanged();
}
#endregion
#region Binding Property
public bool BindsDirectlyToSource { get; set; }
public IValueConverter Converter { get; set; }
public CultureInfo ConverterCulture { get; set; }
public object ConverterParameter { get; set; }
public string ElementName { get; set; }
public BindingMode Mode { get; set; }
public bool NotifyOnValidationError { get; set; }
public string Path { get; set; }
public RelativeSource RelativeSource { get; set; }
public object Source { get; set; }
public UpdateSourceTrigger UpdateSourceTrigger { get; set; }
public bool ValidatesOnDataErrors { get; set; }
public bool ValidatesOnExceptions { get; set; }
public bool ValidatesOnNotifyDataErrors { get; set; }
#endregion
Binding _binding;
public event Action<object, object> ValueChanged;
protected void OnValueChanged()
{
var hander = this.ValueChanged;
if (hander != null)
{
hander(this, Value);
}
}
public void Init()
{
_binding = new Binding();
_binding.BindsDirectlyToSource = this.BindsDirectlyToSource;
_binding.Converter = this.Converter;
_binding.ConverterCulture = this.ConverterCulture;
_binding.ConverterParameter = this.ConverterParameter;
if (!string.IsNullOrEmpty(this.ElementName))
{
_binding.ElementName = this.ElementName;
}
_binding.Mode = this.Mode;
_binding.NotifyOnValidationError = this.NotifyOnValidationError;
_binding.Path = new PropertyPath(this.Path);
if (string.IsNullOrEmpty(_binding.ElementName) && this.RelativeSource != null)
{
_binding.RelativeSource = this.RelativeSource;
}
if (string.IsNullOrEmpty(_binding.ElementName) && this.RelativeSource == null)
{
_binding.Source = this.Source;
}
_binding.UpdateSourceTrigger = this.UpdateSourceTrigger;
_binding.ValidatesOnDataErrors = this.ValidatesOnDataErrors;
_binding.ValidatesOnExceptions = this.ValidatesOnExceptions;
_binding.ValidatesOnNotifyDataErrors = this.ValidatesOnNotifyDataErrors;
BindingOperations.SetBinding(this, ValueProperty, _binding);
}
}这里面直接将Binding的属性复制了一遍,然后实现一个Binding的实例并绑定到了Value这个中间值上。这么做的目的是可以当绑定的值出现变化时这边可以得到通知,然后更新界面。
绑定集合的代码:[csharp] view plaincopyprint?public class BindingCollection : DependencyObjectCollection<BindingSource>
{
public event Action<object, object> HasValueChanged;
public BindingCollection()
{
this.CollectionChanged += BindingCollection_CollectionChanged;
}
void BindingCollection_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null && e.NewItems.Count > 0)
{
foreach (BindingSource binding in e.NewItems)
{
binding.Init();
binding.ValueChanged += binding_ValueChanged;
}
}
if (e.OldItems != null && e.OldItems.Count > 0)
{
foreach (BindingSource binding in e.OldItems)
{
binding.ValueChanged -= this.binding_ValueChanged;
}
}
}
void binding_ValueChanged(object sender, object value)
{
var hander = this.HasValueChanged;
if (hander != null)
{
hander(sender, value);
}
}
}
public class BindingCollection : DependencyObjectCollection<BindingSource>
{
public event Action<object, object> HasValueChanged;
public BindingCollection()
{
this.CollectionChanged += BindingCollection_CollectionChanged;
}
void BindingCollection_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null && e.NewItems.Count > 0)
{
foreach (BindingSource binding in e.NewItems)
{
binding.Init();
binding.ValueChanged += binding_ValueChanged;
}
}
if (e.OldItems != null && e.OldItems.Count > 0)
{
foreach (BindingSource binding in e.OldItems)
{
binding.ValueChanged -= this.binding_ValueChanged;
}
}
}
void binding_ValueChanged(object sender, object value)
{
var hander = this.HasValueChanged;
if (hander != null)
{
hander(sender, value);
}
}
} 剩下的就是将绑定列表通过转换器后更新界面了:[csharp] view plaincopyprint?internal void Source_ValueChanged(object sender, object value)
{
if (Converter == null || AssociatedObject == null || (TargetDependencyProperty == null && TargetPropertyInfo == null))
return;
var targetValue = Converter.Convert(Bindings, TargetPropertyType, ConverterParameter, null);
if (TargetDependencyProperty != null)//若目标是依赖属性则直接赋值
{
AssociatedObject.SetValue(TargetDependencyProperty, targetValue);
}
else if (TargetPropertyInfo != null && TargetPropertyInfo.CanWrite)//非依赖属性通过反射器赋值
{
TargetPropertyInfo.SetValue(this.AssociatedObject, targetValue, null);
}
}
internal void Source_ValueChanged(object sender, object value)
{
if (Converter == null || AssociatedObject == null || (TargetDependencyProperty == null && TargetPropertyInfo == null))
return;
var targetValue = Converter.Convert(Bindings, TargetPropertyType, ConverterParameter, null);
if (TargetDependencyProperty != null)//若目标是依赖属性则直接赋值
{
AssociatedObject.SetValue(TargetDependencyProperty, targetValue);
}
else if (TargetPropertyInfo != null && TargetPropertyInfo.CanWrite)//非依赖属性通过反射器赋值
{
TargetPropertyInfo.SetValue(this.AssociatedObject, targetValue, null);
}
}使用的时候需要自定义一个转换器:[csharp] view plaincopyprint?public class NameConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo language)
{
var values = value as BindingCollection;
if (values == null || values.Count < 2)
return "";
return string.Format("{0}.{1}", values[0].Value, values[1].Value);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo language)
{
return "";
}
}
public class NameConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo language)
{
var values = value as BindingCollection;
if (values == null || values.Count < 2)
return "";
return string.Format("{0}.{1}", values[0].Value, values[1].Value);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo language)
{
return "";
}
}然后xaml中这样使用就可以了:[html] view plaincopyprint?<TextBlock Margin="10" Height="80" VerticalAlignment="Top">
<local:MultiBindings.BindinCollection>
<local:MultiBinding TargetProperty="Text" Converter="{StaticResource NameConverter}">
<local:BindingSource Path="FristName" Source="{StaticResource ViewModel}"/>
<local:BindingSource Path="SecondName" Source="{StaticResource ViewModel}"/>
</local:MultiBinding>
</local:MultiBindings.BindinCollection>
</TextBlock>
<TextBlock Margin="10" Height="80" VerticalAlignment="Top">
<local:MultiBindings.BindinCollection>
<local:MultiBinding TargetProperty="Text" Converter="{StaticResource NameConverter}">
<local:BindingSource Path="FristName" Source="{StaticResource ViewModel}"/>
<local:BindingSource Path="SecondName" Source="{StaticResource ViewModel}"/>
</local:MultiBinding>
</local:MultiBindings.BindinCollection>
</TextBlock>
界面效果
下面的2个按钮点一下上面对应的值+1
示例在个人资源中。
相关文章
- 暂无相关文章
用户评论