自定义Binding

A base class for custom WPF binding markup extensions

BindingDecoratorBase

Code:

public class LookupExtension : BindingDecoratorBase
{
//A property that can be set in XAML
public string LookupKey { get; set; } public override object ProvideValue(IServiceProvider provider)
{
//delegate binding creation etc. to the base class
object val = base.ProvideValue(provider); //try to get bound items for our custom work
DependencyObject targetObject;
DependencyProperty targetProperty;
bool status = TryGetTargetItems(provider, out targetObject,
out targetProperty); if (status)
{
//associate an input listener with the control
InputHandler.RegisterHandler(targetObject, LookupKey);
} return val;
}
}

 

XAML:

<TextBox Name="txtZipCode">
<TextBox.Text>
<local:LookupExtension Source="{StaticResource MyAddress}"
Path="ZipCode"
LookupKey="F5" />
</TextBox.Text>
</TextBox>

效果图:

---------------------------------------------===================================------------------------------

DelayBinding: a custom WPF Binding

 

 

<TextBox Text="{z:DelayBinding Path=SearchText, Delay='00:00:01'}" />

 

[MarkupExtensionReturnType(typeof(object))]
public class DelayBindingExtension : MarkupExtension
{
public DelayBindingExtension()
{
Delay = TimeSpan.FromSeconds(0.5);
} public DelayBindingExtension(PropertyPath path)
: this()
{
Path = path;
} public IValueConverter Converter { get; set; }
public object ConverterParamter { get; set; }
public string ElementName { get; set; }
public RelativeSource RelativeSource { get; set; }
public object Source { get; set; }
public bool ValidatesOnDataErrors { get; set; }
public bool ValidatesOnExceptions { get; set; }
public TimeSpan Delay { get; set; }
[ConstructorArgument("path")]
public PropertyPath Path { get; set; }
[TypeConverter(typeof(CultureInfoIetfLanguageTagConverter))]
public CultureInfo ConverterCulture { get; set; } public override object ProvideValue(IServiceProvider serviceProvider)
{
var valueProvider = serviceProvider.GetService(typeof (IProvideValueTarget)) as IProvideValueTarget;
if (valueProvider != null)
{
var bindingTarget = valueProvider.TargetObject as DependencyObject;
var bindingProperty = valueProvider.TargetProperty as DependencyProperty;
if (bindingProperty == null || bindingTarget == null)
{
throw new NotSupportedException(string.Format(
"The property '{0}' on target '{1}' is not valid for a DelayBinding. The DelayBinding target must be a DependencyObject, "
+ "and the target property must be a DependencyProperty.",
valueProvider.TargetProperty,
valueProvider.TargetObject));
} var binding = new Binding();
binding.Path = Path;
binding.Converter = Converter;
binding.ConverterCulture = ConverterCulture;
binding.ConverterParameter = ConverterParamter;
if (ElementName != null) binding.ElementName = ElementName;
if (RelativeSource != null) binding.RelativeSource = RelativeSource;
if (Source != null) binding.Source = Source;
binding.ValidatesOnDataErrors = ValidatesOnDataErrors;
binding.ValidatesOnExceptions = ValidatesOnExceptions; return DelayBinding.SetBinding(bindingTarget, bindingProperty, Delay, binding);
}
return null;
}
}
public class DelayBinding
{
private readonly BindingExpressionBase _bindingExpression;
private readonly DispatcherTimer _timer; protected DelayBinding(BindingExpressionBase bindingExpression, DependencyObject bindingTarget, DependencyProperty bindingTargetProperty, TimeSpan delay)
{
_bindingExpression = bindingExpression; // Subscribe to notifications for when the target property changes. This event handler will be
// invoked when the user types, clicks, or anything else which changes the target property
var descriptor = DependencyPropertyDescriptor.FromProperty(bindingTargetProperty, bindingTarget.GetType());
descriptor.AddValueChanged(bindingTarget, BindingTarget_TargetPropertyChanged); // Add support so that the Enter key causes an immediate commit
var frameworkElement = bindingTarget as FrameworkElement;
if (frameworkElement != null)
{
frameworkElement.KeyUp += BindingTarget_KeyUp;
} // Setup the timer, but it won't be started until changes are detected
_timer = new DispatcherTimer();
_timer.Tick += Timer_Tick;
_timer.Interval = delay;
} private void BindingTarget_KeyUp(object sender, KeyEventArgs e)
{
if (e.Key != Key.Enter) return;
_timer.Stop();
_bindingExpression.UpdateSource();
} private void BindingTarget_TargetPropertyChanged(object sender, EventArgs e)
{
_timer.Stop();
_timer.Start();
} private void Timer_Tick(object sender, EventArgs e)
{
_timer.Stop();
_bindingExpression.UpdateSource();
} public static object SetBinding(DependencyObject bindingTarget, DependencyProperty bindingTargetProperty, TimeSpan delay, Binding binding)
{
// Override some specific settings to enable the behavior of delay binding
binding.Mode = BindingMode.TwoWay;
binding.UpdateSourceTrigger = UpdateSourceTrigger.Explicit; // Apply and evaluate the binding
var bindingExpression = BindingOperations.SetBinding(bindingTarget, bindingTargetProperty, binding); // Setup the delay timer around the binding. This object will live as long as the target element lives, since it subscribes to the changing event,
// and will be garbage collected as soon as the element isn't required (e.g., when it's Window closes) and the timer has stopped.
new DelayBinding(bindingExpression, bindingTarget, bindingTargetProperty, delay); // Return the current value of the binding (since it will have been evaluated because of the binding above)
return bindingTarget.GetValue(bindingTargetProperty);
}
}

 

参考

Automatically validating business entities in WPF using custom binding and attributes

Flexible and Powerful Data Binding with WPF, Part 2