[WPF系列]-DataBinding(数据绑定) 自定义Binding

自定义Binding

A base class for custom WPF binding markup extensions

BindingDecoratorBase

image

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>

效果图:

Markup Extension Sample

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

DelayBinding: a custom WPF Binding

...but after the short delay, the results change

<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

原文地址:https://www.cnblogs.com/HQFZ/p/4143149.html