stub out things to get the beatles xbox site up and running
[moon.git] / class / System.Windows / System.Windows.Data / BindingExpressionBase.cs
blob7f2fdc880444fbf1eaa997ac3aa18ee6f12e8e1e
1 //
2 // BindingExpressionBase.cs
3 //
4 // Contact:
5 // Moonlight List (moonlight-list@lists.ximian.com)
6 //
7 // Copyright 2008 Novell, Inc.
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 using System.Collections;
30 using System.ComponentModel;
31 using System.Reflection;
32 using System.Windows;
33 using System.Collections.Generic;
34 using System.Windows.Controls;
36 using Mono;
38 namespace System.Windows.Data {
40 public abstract class BindingExpressionBase : Expression
42 internal bool cached;
43 object cachedValue;
44 INotifyPropertyChanged cachedSource;
46 bool parsedPath;
47 PropertyInfo info;
48 bool updatingSource;
50 internal Binding Binding {
51 get; private set;
54 FrameworkElement Target {
55 get; set;
58 internal bool UpdatingSource {
59 get { return updatingSource; }
62 DependencyProperty Property {
63 get; set;
66 bool TwoWayTextBoxText {
67 get { return Target is TextBox && Property == TextBox.TextProperty && Binding.Mode == BindingMode.TwoWay; }
70 // This is the object we're databound to
71 internal object DataSource {
72 get {
73 object source = null;
74 if (Binding.Source != null)
75 source = Binding.Source;
77 // If DataContext is bound, then we need to read the parents datacontext or use null
78 if (source == null && Target != null) {
79 if (Property == FrameworkElement.DataContextProperty) {
80 FrameworkElement e = Target.Parent as FrameworkElement;
81 if (e != null) {
82 source = e.DataContext;
84 } else {
85 source = Target.DataContext;
89 // If the datasource has changed, disconnect from the old object and reconnect
90 // to the new one.
91 if (source != cachedSource) {
92 if (cachedSource != null)
93 cachedSource.PropertyChanged -= PropertyChanged;
94 cachedSource = source as INotifyPropertyChanged;
95 if (cachedSource != null)
96 cachedSource.PropertyChanged += PropertyChanged;
98 return source;
102 PropertyInfo PropertyInfo {
103 get {
104 if (!parsedPath) {
105 parsedPath = true;
106 info = GetPropertyInfo ();
109 return info;
113 // This is the object at the end of the PropertyPath
114 internal object PropertySource {
115 get; private set;
118 internal int PropertyIndexer {
119 get; private set;
122 internal BindingExpressionBase (Binding binding, FrameworkElement target, DependencyProperty property)
124 Binding = binding;
125 Target = target;
126 Property = property;
128 if (TwoWayTextBoxText)
129 ((TextBox) target).LostFocus += TextBoxLostFocus;
131 PropertyIndexer = -1;
134 internal override void Dispose ()
136 if (TwoWayTextBoxText)
137 ((TextBox) Target).LostFocus -= TextBoxLostFocus;
139 if (cachedSource != null)
140 cachedSource.PropertyChanged -= PropertyChanged;
142 cachedSource = null;
145 PropertyInfo GetPropertyInfo ()
147 object source = DataSource;
149 if (source == null)
150 return null;
152 // FIXME: What if the path is invalid? A.B....C, AB.C£$.D etc
153 // Can you have an explicit interface implementation? Probably not.
154 string[] parts = Binding.Path.Path.Split (new char[] { '.' });
155 if (parts.Length == 0) {
156 return null;
158 for (int i = 0; i < parts.Length; i++) {
159 string prop_name = parts [i];
160 string indexer = null;
161 PropertyInfo p = null;
162 int idx = -1;
164 int close = parts [i].LastIndexOf (']');
165 if (close > -1) {
166 int open = parts [i].LastIndexOf ("[");
167 prop_name = parts [i].Substring (0, open);
168 indexer = parts [i].Substring (open + 1, close - open - 1);
172 p = source.GetType ().GetProperty (prop_name);
174 // The property does not exist, so abort.
175 if (p == null)
176 return null;
178 if (!p.DeclaringType.IsVisible)
179 throw new MethodAccessException (string.Format ("Property {0} cannot be accessed", p.Name));
181 if (indexer != null) {
182 if (!Int32.TryParse (indexer, out idx))
183 throw new ArgumentException ("Invalid value for indexer.");
186 if (i != (parts.Length - 1)) {
187 if (indexer != null) {
188 IList list = p.GetValue (source, null) as IList;
189 if (list == null)
190 throw new ArgumentException ("Indexer on non list type.");
191 source = list [idx];
192 } else
193 source = p.GetValue (source, null);
194 if (source == null)
195 return null;
196 else
197 continue;
200 PropertySource = source;
201 PropertyIndexer = idx;
202 return p;
205 throw new Exception ("Should not be reached");
208 public void Invalidate ()
210 cached = false;
211 cachedValue = null;
212 info = null;
213 parsedPath = false;
216 internal override object GetValue (DependencyProperty dp)
218 if (cached)
219 return cachedValue;
221 cached = true;
222 if (DataSource == null) {
223 cachedValue = dp.DefaultValue;
224 return cachedValue;
226 else if (string.IsNullOrEmpty (Binding.Path.Path)) {
227 // If the path is empty, return the active DataSource
228 cachedValue = DataSource;
230 else if (PropertyInfo == null) {
231 cachedValue = dp.DefaultValue;
232 return cachedValue;
234 else {
235 cachedValue = GetPropertyValue ();
237 try {
238 cachedValue = ConvertToType (dp, cachedValue);
239 } catch {
240 cachedValue = dp.DefaultValue;
243 return cachedValue;
246 internal void SetValue (object value)
248 try {
249 // TextBox.Text only updates a two way binding if it is *not* focused.
250 if (TwoWayTextBoxText && System.Windows.Input.FocusManager.GetFocusedElement () == Target)
251 return;
253 if (updatingSource || PropertyInfo == null)
254 return;
256 if (Binding.Converter != null)
257 value = Binding.Converter.ConvertBack (value,
258 PropertyInfo.PropertyType,
259 Binding.ConverterParameter,
260 Binding.ConverterCulture ?? Helper.DefaultCulture);
262 value = MoonlightTypeConverter.ConvertObject (PropertyInfo, value, Target.GetType ());
263 if (cachedValue == null) {
264 if (value == null)
265 return;
267 else if (cachedValue.Equals (value)) {
268 return;
271 updatingSource = true;
272 SetPropertyValue (value);
273 cachedValue = value;
274 } catch (Exception ex) {
275 if (Binding.NotifyOnValidationError && Binding.ValidatesOnExceptions) {
276 Target.RaiseBindingValidationError (new ValidationErrorEventArgs (ValidationErrorEventAction.Added, new ValidationError (ex)));
279 finally {
280 updatingSource = false;
284 void PropertyChanged (object sender, PropertyChangedEventArgs e)
286 try {
287 updatingSource = true;
288 if (string.IsNullOrEmpty (Binding.Path.Path)) {
289 Target.SetValueImpl (Property, ConvertToType (Property, DataSource));
290 } else if (PropertyInfo == null) {
291 return;
292 } else if (PropertyInfo.Name.Equals (e.PropertyName)) {
293 object value = ConvertToType (Property, GetPropertyValue ());
294 Target.SetValueImpl (Property, value);
296 } catch {
297 //Type conversion exceptions are silently swallowed
298 } finally {
299 updatingSource = false;
303 object ConvertToType (DependencyProperty dp, object value)
305 if (Binding.Converter != null) {
306 value = Binding.Converter.Convert (value,
307 Property.PropertyType,
308 Binding.ConverterParameter,
309 Binding.ConverterCulture ?? Helper.DefaultCulture);
311 return MoonlightTypeConverter.ConvertObject (dp, value, Target.GetType ());
314 void TextBoxLostFocus (object sender, RoutedEventArgs e)
316 SetValue (((TextBox) sender).Text);
319 object GetPropertyValue ()
321 object res = PropertyInfo.GetValue (PropertySource, null);
323 if (res != null && PropertyIndexer != -1) {
324 IList list = res as IList;
325 if (list == null)
326 throw new ArgumentException ("Indexer on non list type");
327 res = list [PropertyIndexer];
330 return res;
333 void SetPropertyValue (object value)
335 if (PropertyIndexer == -1) {
336 PropertyInfo.SetValue (PropertySource, value, null);
337 return;
340 object source = PropertyInfo.GetValue (PropertySource, null);
342 if (source != null && PropertyIndexer != -1) {
343 IList list = source as IList;
344 if (list == null)
345 throw new ArgumentException ("Indexer on non list type");
346 list [PropertyIndexer] = value;