Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
OnFocusBindingInterruptionBehavior.cs
Go to the documentation of this file.
1 // Copyright (c) 2014 Silicon Studio Corp. (http://siliconstudio.co.jp)
2 // This file is distributed under GPL v3. See LICENSE.md for details.
3 using System;
4 using System.Collections.Generic;
5 using System.Linq;
6 using System.Text;
7 using System.Windows.Controls;
8 using System.Windows;
9 using System.Windows.Data;
10 using System.Windows.Interactivity;
11 using System.ComponentModel;
12 
13 using SiliconStudio.Core;
14 using SiliconStudio.Presentation.Core;
15 using SiliconStudio.Presentation.Extensions;
16 
17 namespace SiliconStudio.Presentation.Behaviors
18 {
19  /// <summary>
20  /// Represents a behavior capable of interrupting a <c>Binding</c> on a <c>UIElement</c> when it receives focus,
21  /// and resuming that <c>Binding</c> when it loses focus.
22  /// </summary>
23  /// <remarks>The host element must be of type <c>UIElement</c> and the <c>DependencyProprty</c> defined by PropertyName property must be set to a <c>BindingBase</c> object.</remarks>
24  public class OnFocusBindingInterruptionBehavior : Behavior<DependencyObject>
25  {
26  private IDisposable subscriber;
27  private DependencyProperty property;
28 
29  /// <summary>
30  /// Gets or sets the name of the DependencyProperty on which the Binding has to be interrupted.
31  /// </summary>
32  public string PropertyName { get; set; }
33  /// <summary>
34  /// Gets or sets the binding to apply on the Host property defined by 'PropertyName' behavior property.
35  /// </summary>
36  public BindingBase Binding { get; set; }
37 
38  protected override void OnAttached()
39  {
40  if (string.IsNullOrWhiteSpace(PropertyName))
41  throw new ArgumentException("PropertyName must be set.");
42 
43  if (Binding == null)
44  {
45  throw new ArgumentException(string.Format("Binding must be set for {0} property of host '{1}' on behavior '{2}'.",
46  PropertyName, AssociatedObject, this.GetType().FullName));
47  }
48 
49  property = AssociatedObject.GetDependencyProperties(true).FirstOrDefault(dp => dp.Name == PropertyName);
50 
51  if (property == null /* need to check DesignMode as well ? */)
52  throw new InvalidOperationException(string.Format("Impossible to find property named '{0}' on object typed '{1}'.", PropertyName, AssociatedObject.GetType()));
53 
54  if ((Binding is Binding) == false)
55  throw new InvalidOperationException("Not supported binding type.");
56 
57  var element = AssociatedObject as FrameworkElement;
58  if (element == null)
59  {
60  throw new InvalidOperationException(string.Format("Behavior of type '{0}' must be bound to objects of type '{1}'. (currently bound to object typed '{2}')",
61  this.GetType(), typeof(FrameworkElement), AssociatedObject.GetType()));
62  }
63 
64  BindingOperations.SetBinding(AssociatedObject, property, Binding);
65 
66  // subscribe to *Focus events
67  element.GotFocus += OnHostGotFocus;
68  element.LostFocus += OnHostLostFocus;
69 
70  subscriber = new AnonymousDisposable(() =>
71  {
72  // unsubscribe from *Focus events
73  element.LostFocus -= OnHostLostFocus;
74  element.GotFocus -= OnHostGotFocus;
75  });
76  }
77 
78  protected override void OnDetaching()
79  {
80  // fire events unsubscription
81  subscriber.Dispose();
82  }
83 
84  private void OnHostGotFocus(object sender, RoutedEventArgs e)
85  {
86  // retrieve current value before clearing binding
87  object value = AssociatedObject.GetValue(property);
88 
89  // clear binding (the side-effect is that value is also clear from within the control)
90  BindingOperations.ClearBinding(AssociatedObject, property);
91 
92  // set back value stored before clearing binding
93  AssociatedObject.SetValue(property, value);
94  }
95 
96  private void OnHostLostFocus(object sender, RoutedEventArgs e)
97  {
98  PushTargetValueToSource();
99  }
100 
101  // this method may not support several specific cases (to be improved)
102  private void PushTargetValueToSource()
103  {
104  // retrieve the current value of the target (UI control)
105  object currentValue = AssociatedObject.GetValue(property);
106 
107  var binding = (Binding)Binding;
108 
109  // resolve the source instance here (seems BindingOperations.SetBinding does not resolve DataContext)
110  object source = binding.Source ?? ((FrameworkElement)AssociatedObject).DataContext;
111 
112  var intermediateBinding = new Binding
113  {
114  // update on PropertyChanged because LostFocus event already happened
115  UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,
116 
117  // ensure binding cannot be perturbated by source value
118  Mode = BindingMode.OneWayToSource,
119 
120  Path = binding.Path,
121  Source = source,
122 
123  Converter = binding.Converter,
124  ConverterParameter = binding.ConverterParameter,
125  };
126 
127  // apply custom binding
128  BindingOperations.SetBinding(AssociatedObject, property, intermediateBinding);
129 
130  // set target property, side-effect is the source property value is set too
131  AssociatedObject.SetValue(property, currentValue);
132 
133  // restore cleared binding
134  BindingOperations.SetBinding(AssociatedObject, property, Binding);
135  }
136  }
137 }
This class allows implementation of IDisposable using anonymous functions. The anonymous function wil...
Represents a behavior capable of interrupting a Binding on a UIElement when it receives focus...