2 // ManagedXamlLoader.cs
5 // Moonlight List (moonlight-list@lists.ximian.com)
7 // Copyright 2007 Novell, Inc.
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:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
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.
30 using System
.Collections
;
31 using System
.Diagnostics
;
32 using System
.Reflection
;
33 using System
.Collections
.Generic
;
36 using System
.Windows
.Controls
;
37 using System
.Windows
.Markup
;
38 using System
.Runtime
.InteropServices
;
39 using System
.ComponentModel
;
40 using System
.Windows
.Data
;
41 using System
.Text
.RegularExpressions
;
46 internal class ManagedXamlLoader
: XamlLoader
{
49 XamlLoaderCallbacks callbacks
;
52 public ManagedXamlLoader ()
56 public ManagedXamlLoader (Assembly assembly
, string resourceBase
, IntPtr surface
, IntPtr plugin
) : base (resourceBase
, surface
, plugin
)
58 this.assembly
= assembly
;
61 public override void Setup (IntPtr native_loader
, IntPtr plugin
, IntPtr surface
, string filename
, string contents
)
63 base.Setup (native_loader
, plugin
, surface
, filename
, contents
);
66 // Registers callbacks that are invoked from the
70 callbacks
.lookup_object
= new LookupObjectCallback (cb_lookup_object
);
71 callbacks
.create_gchandle
= new CreateGCHandleCallback (cb_create_gchandle
);
72 callbacks
.set_property
= new SetPropertyCallback (cb_set_property
);
73 callbacks
.import_xaml_xmlns
= new ImportXamlNamespaceCallback (cb_import_xaml_xmlns
);
74 callbacks
.get_content_property_name
= new GetContentPropertyNameCallback (cb_get_content_property_name
);
75 callbacks
.add_child
= new AddChildCallback (cb_add_child
);
78 NativeMethods
.xaml_loader_set_callbacks (native_loader
, callbacks
);
80 if (plugin
!= IntPtr
.Zero
)
81 System
.Windows
.Interop
.PluginHost
.SetPluginHandle (plugin
);
83 if (!AllowMultipleSurfacesPerDomain
) {
84 PluginInDomain
= plugin
;
85 SurfaceInDomain
= surface
;
90 // Creates a managed dependency object from the xaml.
92 public override object CreateObjectFromString (string xaml
, bool createNamescope
)
94 return CreateObjectFromString (xaml
, createNamescope
, false);
97 public override object CreateObjectFromString (string xaml
, bool createNamescope
, bool validateTemplates
)
100 throw new ArgumentNullException ("xaml");
106 DependencyObject
.Initialize ();
108 top
= CreateFromString (xaml
, createNamescope
, validateTemplates
, out kind
);
110 if (top
== IntPtr
.Zero
)
113 result
= Value
.ToObject (null, top
);
114 DependencyObject dob
= result
as DependencyObject
;
116 NativeMethods
.event_object_unref (dob
.native
);
123 // Creates a managed dependency object from the xaml in the file
125 public override object CreateObjectFromFile (string file
, bool createNamescope
)
128 throw new ArgumentNullException ("file");
134 DependencyObject
.Initialize ();
136 top
= CreateFromFile (file
, createNamescope
, out kind
);
138 if (top
== IntPtr
.Zero
)
141 result
= Value
.ToObject (null, top
);
142 DependencyObject dob
= result
as DependencyObject
;
144 NativeMethods
.event_object_unref (dob
.native
);
151 // Tries to load the assembly.
153 public AssemblyLoadResult
LoadAssembly (string asm_name
, out Assembly clientlib
)
157 clientlib
= Application
.GetAssembly (asm_name
);
158 return clientlib
!= null ? AssemblyLoadResult
.Success
: AssemblyLoadResult
.MissingAssembly
;
161 private unsafe bool TryGetDefaultAssemblyName (Value
* top_level
, out string assembly_name
)
163 if (assembly
!= null) {
164 assembly_name
= assembly
.GetName ().Name
;
168 object obj
= Value
.ToObject (null, top_level
);
171 assembly_name
= null;
175 assembly_name
= obj
.GetType ().Assembly
.GetName ().Name
;
179 private unsafe bool LookupObject (Value
*top_level
, Value
*parent
, string xmlns
, string name
, bool create
, bool is_property
, out Value
value)
182 throw new ArgumentNullException ("type_name");
186 int dot
= name
.IndexOf (".");
187 return LookupPropertyObject (top_level
, parent
, xmlns
, name
, dot
, create
, out value);
190 if (top_level
== null && xmlns
== null) {
191 return LookupComponentFromName (top_level
, name
, create
, out value);
194 string assembly_name
= AssemblyNameFromXmlns (xmlns
);
195 string clr_namespace
= ClrNamespaceFromXmlns (xmlns
);
196 string full_name
= string.IsNullOrEmpty (clr_namespace
) ? name
: clr_namespace
+ "." + name
;
198 Type type
= LookupType (top_level
, assembly_name
, full_name
);
200 Console
.Error
.WriteLine ("ManagedXamlLoader::LookupObject: GetType ({0}) failed using assembly: {1} ({2}, {3}).", name
, assembly_name
, xmlns
, full_name
);
207 if (!type
.IsPublic
) {
209 throw new XamlParseException ("Attempting to create a private type");
214 res
= Activator
.CreateInstance (type
);
215 } catch (TargetInvocationException ex
) {
216 Console
.WriteLine (ex
);
217 Console
.Error
.WriteLine ("ManagedXamlLoader::LookupObject: CreateInstance ({0}) failed: {1}", name
, ex
.InnerException
);
223 Console
.Error
.WriteLine ("ManagedXamlLoader::LookupObject ({0}, {1}, {2}): unable to create object instance: '{3}', the object was of type '{4}'",
224 assembly_name
, xmlns
, name
, full_name
, type
.FullName
);
228 value = Value
.FromObject (res
, false);
231 value.k
= Deployment
.Current
.Types
.Find (type
).native_handle
;
237 private unsafe bool LookupPropertyObject (Value
* top_level
, Value
* parent_value
, string xmlns
, string name
, int dot
, bool create
, out Value
value)
239 string prop_name
= name
.Substring (dot
+ 1);
240 object parent
= Value
.ToObject (null, parent_value
);
242 if (parent
== null) {
247 PropertyInfo pi
= null;
248 bool is_attached
= true;
249 string type_name
= name
.Substring (0, dot
);
251 Type t
= parent
.GetType ();
252 while (t
!= typeof (object)) {
253 if (t
.Name
== type_name
) {
261 MethodInfo set_method
= GetSetMethodForAttachedProperty (top_level
, xmlns
, type_name
, prop_name
);
263 ParameterInfo
[] set_params
= set_method
.GetParameters ();
264 if (set_params
== null || set_params
.Length
< 2) {
266 Console
.Error
.WriteLine ("set method signature is incorrect.");
270 ManagedType mt
= Deployment
.Current
.Types
.Find (set_params
[1].ParameterType
);
273 value.k
= mt
.native_handle
;
277 pi
= parent
.GetType ().GetProperty (name
.Substring (dot
+ 1), BindingFlags
.Instance
| BindingFlags
.NonPublic
| BindingFlags
.Public
| BindingFlags
.FlattenHierarchy
);
284 ManagedType mt
= Deployment
.Current
.Types
.Find (pi
.PropertyType
);
286 value.k
= mt
.native_handle
;
293 private unsafe bool LookupComponentFromName (Value
* top_level
, string name
, bool create
, out Value
value)
296 Type type
= Application
.GetComponentTypeFromName (name
);
302 value.k
= Deployment
.Current
.Types
.Find (type
).native_handle
;
306 object obj
= Application
.CreateComponentFromName (name
);
312 value = Value
.FromObject (obj
, false);
316 private static bool IsAttachedProperty (string name
)
318 return name
.IndexOf ('.') > 0;
321 private unsafe DependencyProperty
LookupDependencyPropertyForBinding (XamlCallbackData
*data
, FrameworkElement fwe
, string type_name
, string propertyName
)
323 // map the property name + type_name to an actual DependencyProperty
325 Type type
= string.IsNullOrEmpty (type_name
) ? null : TypeFromString (data
, type_name
);
328 kind
= Deployment
.Current
.Types
.TypeToNativeKind (type
);
330 kind
= fwe
.GetKind ();
333 if (kind
== Kind
.INVALID
)
337 return DependencyProperty
.Lookup (kind
, propertyName
);
345 private unsafe bool TrySetExpression (XamlCallbackData
*data
, string xmlns
, object target
, IntPtr target_data
, Value
* target_parent_ptr
, string type_name
, string prop_xmlns
, string name
, string full_name
, Value
* value_ptr
, IntPtr value_data
)
347 FrameworkElement dob
= target
as FrameworkElement
;
348 object obj_value
= Value
.ToObject (null, value_ptr
);
349 string str_value
= obj_value
as string;
351 if (str_value
== null)
354 if (!str_value
.StartsWith ("{"))
357 MarkupExpressionParser p
= new MarkupExpressionParser (target
, name
, data
->parser
, target_data
);
358 string expression
= str_value
;
359 object o
= p
.ParseExpression (ref expression
);
366 Binding binding
= o
as Binding
;
367 DependencyProperty prop
= null;
370 prop
= LookupDependencyPropertyForBinding (data
, dob
, type_name
, name
);
375 dob
.SetBinding (prop
, binding
);
378 else if (o
is TemplateBindingExpression
) {
379 // Applying a {TemplateBinding} to a DO which is not a FrameworkElement should silently discard
384 TemplateBindingExpression tb
= o
as TemplateBindingExpression
;
386 IntPtr context
= NativeMethods
.xaml_loader_get_context (data
->loader
);
387 IntPtr source_ptr
= NativeMethods
.xaml_context_get_template_binding_source (context
);
389 DependencyObject templateSourceObject
= NativeDependencyObjectHelper
.FromIntPtr (source_ptr
) as DependencyObject
;
391 if (templateSourceObject
== null)
394 DependencyProperty sourceProperty
= DependencyProperty
.Lookup (templateSourceObject
.GetKind(),
395 tb
.SourcePropertyName
);
396 if (sourceProperty
== null)
399 DependencyProperty prop
= null;
402 prop
= LookupDependencyPropertyForBinding (data
, dob
, type_name
, name
);
407 tb
.TargetProperty
= prop
;
408 tb
.Source
= templateSourceObject
as Control
;
409 tb
.SourceProperty
= sourceProperty
;
411 dob
.SetTemplateBinding (prop
, tb
);
416 // static resources fall into this
419 DependencyProperty prop
= LookupDependencyPropertyForBinding (data
, dob
, type_name
, name
);
423 o
= ConvertType (null, prop
.PropertyType
, o
);
424 dob
.SetValue (prop
, o
);
426 if (IsAttachedProperty (full_name
))
427 return TrySetAttachedProperty (data
, xmlns
, target
, target_data
, prop_xmlns
, full_name
, o
);
429 PropertyInfo pi
= target
.GetType ().GetProperty (name
, BindingFlags
.Instance
| BindingFlags
.NonPublic
| BindingFlags
.Public
| BindingFlags
.FlattenHierarchy
);
431 o
= ConvertType (null, pi
.PropertyType
, o
);
432 SetValue (data
, target_data
, pi
, target
, o
);
440 private string GetNameForAttachedProperty (string xmlns
, string prop_xmlns
, string name
, out string type_name
)
442 int dot
= name
.IndexOf ('.');
445 type_name
= name
.Substring (0, dot
);
446 if (prop_xmlns
!= null || xmlns
!= null) {
447 string ns
= ClrNamespaceFromXmlns (prop_xmlns
== null ? xmlns
: prop_xmlns
);
449 type_name
= String
.Concat (ns
, ".", type_name
);
451 name
= name
.Substring (++dot
, name
.Length
- dot
);
460 private unsafe bool TrySetAttachedProperty (XamlCallbackData
*data
, string xmlns
, object target
, IntPtr target_data
, string prop_xmlns
, string name
, Value
* value_ptr
)
462 string full_name
= name
;
463 string type_name
= null;
465 name
= GetNameForAttachedProperty (xmlns
, prop_xmlns
, name
, out type_name
);
471 object o_value
= GetObjectValue (target
, target_data
, name
, data
->parser
, value_ptr
, out error
);
473 return TrySetAttachedProperty (data
, xmlns
, target
, target_data
, prop_xmlns
, full_name
, o_value
);
476 private unsafe bool TrySetAttachedProperty (XamlCallbackData
*data
, string xmlns
, object target
, IntPtr target_data
, string prop_xmlns
, string name
, object o_value
)
478 string type_name
= null;
480 name
= GetNameForAttachedProperty (xmlns
, prop_xmlns
, name
, out type_name
);
485 MethodInfo set_method
= GetSetMethodForAttachedProperty (data
->top_level
, prop_xmlns
, type_name
, name
);
486 if (set_method
== null) {
487 Console
.Error
.WriteLine ("set method is null: {0} {1}", String
.Concat ("Set", name
), prop_xmlns
);
491 ParameterInfo
[] set_params
= set_method
.GetParameters ();
492 if (set_params
== null || set_params
.Length
< 2) {
493 Console
.Error
.WriteLine ("set method signature is inccorrect.");
497 MethodInfo get_method
= set_method
.DeclaringType
.GetMethod (String
.Concat ("Get", name
), BindingFlags
.Static
| BindingFlags
.Public
| BindingFlags
.NonPublic
);
500 // The Setter might actually want a collection, in this case we grab the old collection with the getter
501 // and then add the new object to the collection
503 // TODO: Check if the setter method still gets called on Silverlight
504 if (typeof (IList
).IsAssignableFrom (set_params
[1].ParameterType
) && !(o_value
is IList
)) {
506 if (get_method
!= null || get_method
.GetParameters () == null || get_method
.GetParameters ().Length
!= 1) {
507 IList the_list
= (IList
) get_method
.Invoke (null, new object [] { target }
);
509 if (the_list
== null) {
510 the_list
= (IList
) Activator
.CreateInstance (set_params
[1].ParameterType
);
511 if (the_list
== null)
513 set_method
.Invoke (null, new object [] {target, the_list}
);
517 the_list
.Add (o_value
);
519 if (o_value
is DependencyObject
&& target
is DependencyObject
&& !(the_list
is DependencyObject
)) {
520 NativeMethods
.dependency_object_set_parent (((DependencyObject
)o_value
).native
, ((DependencyObject
)target
).native
);
526 // don't return here, fall through to the ConvertType case below.
529 // I guess we need to wrap the current value in a collection, or does this error out?
530 Console
.WriteLine ("ow god my eye!");
535 o_value
= ConvertType (get_method
, set_params
[1].ParameterType
, o_value
);
536 set_method
.Invoke (null, new object [] {target, o_value}
);
540 private unsafe bool TrySetPropertyReflection (XamlCallbackData
*data
, string xmlns
, object target
, IntPtr target_data
, Value
* target_parent_ptr
, string type_name
, string name
, Value
* value_ptr
, IntPtr value_data
, out string error
)
542 PropertyInfo pi
= target
.GetType ().GetProperty (name
, BindingFlags
.Instance
| BindingFlags
.NonPublic
| BindingFlags
.Public
| BindingFlags
.FlattenHierarchy
);
545 error
= "Property does not exist.";
549 if (!SetPropertyFromValue (data
, target
, target_data
, target_parent_ptr
, pi
, value_ptr
, value_data
, out error
))
556 private unsafe bool TrySetEventReflection (XamlCallbackData
*data
, string xmlns
, object publisher
, string type_name
, string name
, Value
* value_ptr
, out string error
)
558 object subscriber
= null;
559 EventInfo ie
= publisher
.GetType ().GetEvent (name
);
560 string handler_name
= Value
.ToObject (null, value_ptr
) as string;
563 subscriber
= Value
.ToObject (null, data
->top_level
);
568 //Console.WriteLine ("TrySetEventReflection ({0}, {1}, {2}, {3}, {4}, {5}) handler_name: {6}", data->top_level, xmlns, publisher, type_name, name, value_ptr, handler_name);
571 error
= "Event does not exist.";
576 if (handler_name
== null) {
577 error
= "No method name supplied for event handler.";
581 MethodInfo invoker_info
= ie
.EventHandlerType
.GetMethod ("Invoke");
582 ParameterInfo
[] event_params
= invoker_info
.GetParameters ();
585 Type stype
= subscriber
.GetType ();
586 MethodInfo
[] methods
= stype
.GetMethods (BindingFlags
.Public
| BindingFlags
.NonPublic
| BindingFlags
.DeclaredOnly
| BindingFlags
.Instance
);
587 MethodInfo candidate
= null;
588 bool name_match
= false;
590 for (int i
= 0; i
< methods
.Length
; i
++) {
591 MethodInfo m
= methods
[i
];
592 ParameterInfo
[] parameters
;
594 if (m
.Name
!= handler_name
)
598 error
= "Multiple candidates with the same name found for event handler.";
599 // Console.WriteLine (error);
605 if (m
.ReturnType
!= typeof (void))
608 parameters
= m
.GetParameters ();
610 if (parameters
.Length
!= event_params
.Length
)
614 for (int p
= 0; p
< parameters
.Length
; p
++) {
615 if (!event_params
[p
].ParameterType
.IsSubclassOf (parameters
[p
].ParameterType
) && parameters
[p
].ParameterType
!= event_params
[p
].ParameterType
) {
616 Console
.WriteLine ("mismatch: {0} and {1}", parameters
[p
].ParameterType
, event_params
[p
].ParameterType
);
625 if (candidate
!= null) {
626 error
= "Multiple candidates for event handler found.";
627 // Console.WriteLine (error);
634 if (candidate
== null) {
635 error
= "Event handler not found.";
636 // Console.WriteLine (error);
640 d
= Delegate
.CreateDelegate (ie
.EventHandlerType
, subscriber
, candidate
, false);
643 Console
.Error
.WriteLine ("ManagedXamlLoader::HookupEvent ({0}, {1}, {2}): unable to create delegate (src={3} target={4}).", (IntPtr
) data
->top_level
, name
, (IntPtr
)value_ptr
, ie
.EventHandlerType
, publisher
);
644 error
= "Can not create even delegate.";
648 // Console.Error.WriteLine ("ManagedXamlLoader::HookupEvent ({0}, {1}, {2}): Successfully created delegate (src={3} target={4}).", (IntPtr) data->top_level, name, value_ptr, ie.EventHandlerType, publisher);
651 ie
.AddEventHandler (publisher
, d
);
655 private unsafe bool TrySetEnumContentProperty (XamlCallbackData
*data
, string xmlns
, object target
, Value
* target_ptr
, IntPtr target_data
, Value
* value_ptr
, IntPtr value_data
)
657 object obj_value
= Value
.ToObject (null, value_ptr
);
658 string str_value
= obj_value
as string;
660 if (str_value
== null)
663 string assembly_name
= AssemblyNameFromXmlns (xmlns
);
664 string clr_namespace
= ClrNamespaceFromXmlns (xmlns
);
665 string type_name
= NativeMethods
.xaml_get_element_name (data
->parser
, target_data
);
666 string full_name
= String
.IsNullOrEmpty (clr_namespace
) ? type_name
: clr_namespace
+ "." + type_name
;
668 Type type
= LookupType (data
->top_level
, assembly_name
, full_name
);
670 if (type
== null || !type
.IsEnum
)
673 object e
= Enum
.Parse (type
, str_value
, true);
675 NativeMethods
.value_free_value2 ((IntPtr
)target_ptr
);
678 Value
*val
= (Value
*) target_ptr
;
680 GCHandle handle
= GCHandle
.Alloc (e
);
681 val
->k
= Kind
.MANAGED
;
682 val
->u
.p
= GCHandle
.ToIntPtr (handle
);
688 private unsafe bool TrySetCollectionContentProperty (string xmlns
, object target
, Value
* target_ptr
, IntPtr target_data
, Value
* value_ptr
, IntPtr value_data
)
690 IList list
= target
as IList
;
695 object value = Value
.ToObject (null, value_ptr
);
701 private unsafe bool TrySetObjectTextProperty (XamlCallbackData
*data
, string xmlns
, object target
, Value
* target_ptr
, IntPtr target_data
, Value
* value_ptr
, IntPtr value_data
)
703 object obj_value
= Value
.ToObject (null, value_ptr
);
704 string str_value
= obj_value
as string;
706 if (str_value
== null)
709 string assembly_name
= AssemblyNameFromXmlns (xmlns
);
710 string clr_namespace
= ClrNamespaceFromXmlns (xmlns
);
711 string type_name
= NativeMethods
.xaml_get_element_name (data
->parser
, target_data
);
712 string full_name
= String
.IsNullOrEmpty (clr_namespace
) ? type_name
: clr_namespace
+ "." + type_name
;
714 Type type
= LookupType (data
->top_level
, assembly_name
, full_name
);
716 if (type
== null || type
.IsSubclassOf (typeof (DependencyObject
)))
719 // For now just trim the string right here, in the future this should probably be done in the xaml parser
720 object e
= ConvertType (null, type
, str_value
.Trim ());
722 NativeMethods
.value_free_value2 ((IntPtr
)target_ptr
);
725 Value
*val
= (Value
*) target_ptr
;
727 GCHandle handle
= GCHandle
.Alloc (e
);
728 val
->k
= Kind
.MANAGED
;
729 val
->u
.p
= GCHandle
.ToIntPtr (handle
);
735 private unsafe bool SetProperty (XamlCallbackData
*data
, string xmlns
, Value
* target_ptr
, IntPtr target_data
, Value
* target_parent_ptr
, string prop_xmlns
, string name
, Value
* value_ptr
, IntPtr value_data
)
738 object target
= Value
.ToObject (null, target_ptr
);
740 if (target
== null) {
741 Console
.Error
.WriteLine ("target is null: {0} {1} {2}", (IntPtr
)target_ptr
, name
, xmlns
);
746 if (TrySetEnumContentProperty (data
, xmlns
, target
, target_ptr
, target_data
, value_ptr
, value_data
))
748 if (TrySetCollectionContentProperty (xmlns
, target
, target_ptr
, target_data
, value_ptr
, value_data
))
750 if (TrySetObjectTextProperty (data
, xmlns
, target
, target_ptr
, target_data
, value_ptr
, value_data
))
752 Console
.Error
.WriteLine ("no property name supplied");
756 string full_name
= name
;
757 int dot
= name
.IndexOf ('.');
758 string type_name
= null;
761 type_name
= name
.Substring (0, dot
);
763 string ns
= ClrNamespaceFromXmlns (xmlns
);
765 type_name
= String
.Concat (ns
, ".", type_name
);
767 name
= name
.Substring (++dot
, name
.Length
- dot
);
770 if (TrySetExpression (data
, xmlns
, target
, target_data
, target_parent_ptr
, type_name
, prop_xmlns
, name
, full_name
, value_ptr
, value_data
))
773 if (TrySetPropertyReflection (data
, xmlns
, target
, target_data
, target_parent_ptr
, type_name
, name
, value_ptr
, value_data
, out error
))
776 if (TrySetEventReflection (data
, xmlns
, target
, type_name
, name
, value_ptr
, out error
))
779 if (TrySetAttachedProperty (data
, xmlns
, target
, target_data
, prop_xmlns
, full_name
, value_ptr
))
785 private unsafe bool AddChild (XamlCallbackData
*data
, Value
* parent_parent_ptr
, bool parent_is_property
, string parent_xmlns
, Value
*parent_ptr
, IntPtr parent_data
, Value
* child_ptr
, IntPtr child_data
)
787 object parent_parent
= Value
.ToObject (null, parent_parent_ptr
);
788 object parent
= Value
.ToObject (null, parent_ptr
);
789 object child
= Value
.ToObject (null, child_ptr
);
791 if (parent_is_property
)
792 return AddChildToProperty (data
, parent_parent
, parent_xmlns
, parent
, child
, child_data
);
794 return AddChildToItem (data
, parent_parent_ptr
, parent
, parent_data
, child_ptr
, child
, child_data
);
797 private unsafe bool AddChildToProperty (XamlCallbackData
*data
, object parent_parent
, string parent_xmlns
, object parent
, object child
, IntPtr child_data
)
799 string full_prop_name
= parent
as string;
801 if (full_prop_name
== null) {
802 Console
.Error
.WriteLine ("Attempting to add child to non string parent {0} as a property.", parent
);
807 int dot
= full_prop_name
.IndexOf ('.');
810 string type_name
= full_prop_name
.Substring (0, dot
);
811 string prop_name
= full_prop_name
.Substring (++dot
, full_prop_name
.Length
- dot
);
813 Type target_type
= TypeFromString (data
, parent_xmlns
, type_name
);
815 if (target_type
== null) {
816 Console
.Error
.WriteLine ("Type '{0}' with xmlns '{1}' could not be found", type_name
, parent_xmlns
);
820 if (!target_type
.IsAssignableFrom (parent_parent
.GetType ())) {
821 // This would happen with an attached property, we don't need to do anything here....do we?
825 PropertyInfo pi
= parent_parent
.GetType ().GetProperty (prop_name
, BindingFlags
.Instance
| BindingFlags
.NonPublic
| BindingFlags
.Public
| BindingFlags
.FlattenHierarchy
);
828 Console
.Error
.WriteLine ("Property does not exist. {0}", prop_name
);
832 if (typeof (ResourceDictionary
).IsAssignableFrom (pi
.PropertyType
) && !(child
is ResourceDictionary
)) {
833 ResourceDictionary the_dict
= (ResourceDictionary
) pi
.GetValue (parent_parent
, null);
834 string key_name
= NativeMethods
.xaml_get_element_key (data
->parser
, child_data
);
836 if (key_name
== null) {
837 throw new XamlParseException (2034, "Elements in a ResourceDictionary must have x:Key or x:Name attribute.");
840 if (the_dict
== null) {
841 the_dict
= (ResourceDictionary
) Activator
.CreateInstance (pi
.PropertyType
);
842 if (the_dict
== null) {
843 Console
.Error
.WriteLine ("Unable to create instance of dictionary: " + pi
.PropertyType
);
846 pi
.SetValue (parent_parent
, the_dict
, null);
850 the_dict
.Add (key_name
, child
);
851 if (child
is DependencyObject
&& parent_parent
is DependencyObject
&& !(the_dict
is DependencyObject
)) {
852 NativeMethods
.dependency_object_set_parent (((DependencyObject
) child
).native
, ((DependencyObject
) parent_parent
).native
);
856 } catch (ArgumentException
) {
857 throw new XamlParseException (2273, "Elements in the same ResourceDictionary cannot have the same x:Key");
861 if (typeof (IList
).IsAssignableFrom (pi
.PropertyType
) && !(child
is IList
)) {
862 IList the_list
= (IList
) pi
.GetValue (parent_parent
, null);
864 if (the_list
== null) {
865 the_list
= (IList
) Activator
.CreateInstance (pi
.PropertyType
);
866 if (the_list
== null) {
867 Console
.Error
.WriteLine ("Unable to create instance of list: " + pi
.PropertyType
);
870 pi
.SetValue (parent_parent
, the_list
, null);
874 the_list
.Add (child
);
876 if (child
is DependencyObject
&& parent_parent
is DependencyObject
&& !(the_list
is DependencyObject
)) {
877 NativeMethods
.dependency_object_set_parent (((DependencyObject
)child
).native
, ((DependencyObject
)parent_parent
).native
);
882 catch (Exception e
) {
883 Console
.WriteLine (e
);
891 private unsafe bool AddChildToItem (XamlCallbackData
*data
, Value
*parent_parent_ptr
, object parent
, IntPtr parent_data
, Value
*child_ptr
, object child
, IntPtr child_data
)
893 ResourceDictionary the_dict
= parent
as ResourceDictionary
;
894 if (the_dict
!= null) {
895 string key_name
= NativeMethods
.xaml_get_element_key (data
->parser
, child_data
);
897 if (key_name
== null) {
898 Console
.Error
.WriteLine ("Attempting to add item to a resource dictionary without an x:Key or x:Name");
899 throw new XamlParseException (-1, -1, "You must specify an x:Key or x:Name for elements in a ResourceDictionary");
903 the_dict
.Add (key_name
, child
);
904 if (child
is DependencyObject
&& parent
is DependencyObject
&& !(the_dict
is DependencyObject
)) {
905 NativeMethods
.dependency_object_set_parent (((DependencyObject
) child
).native
, ((DependencyObject
) parent
).native
);
909 } catch (Exception e
) {
910 // Fall through to string
911 Console
.Error
.WriteLine (e
);
916 IList the_list
= parent
as IList
;
917 if (the_list
!= null) {
920 the_list
.Add (child
);
922 if (child
is DependencyObject
&& parent
is DependencyObject
&& !(the_list
is DependencyObject
)) {
923 NativeMethods
.dependency_object_set_parent (((DependencyObject
)child
).native
, ((DependencyObject
)parent
).native
);
933 Type parent_type
= parent
.GetType ();
934 PropertyInfo pi
= GetContentProperty (parent_type
);
937 Console
.Error
.WriteLine ("Unable to find content property on type {0}", parent_type
);
942 // Is the content property a collection
944 if (typeof (IList
).IsAssignableFrom (pi
.PropertyType
) && !(child
is IList
)) {
945 the_list
= (IList
) pi
.GetValue (parent
, null);
947 if (the_list
== null) {
948 the_list
= (IList
) Activator
.CreateInstance (pi
.PropertyType
);
949 if (the_list
== null) {
950 Console
.Error
.WriteLine ("Unable to create instance of list: " + pi
.PropertyType
);
953 pi
.SetValue (parent
, the_list
, null);
957 the_list
.Add (child
);
959 if (child
is DependencyObject
&& parent
is DependencyObject
&& !(the_list
is DependencyObject
)) {
960 NativeMethods
.dependency_object_set_parent (((DependencyObject
)child
).native
, ((DependencyObject
)parent
).native
);
972 return SetPropertyFromValue (data
, parent
, parent_data
, parent_parent_ptr
, pi
, child_ptr
, child_data
, out error
);
974 throw new XamlParseException (2010, String
.Format ("{0} does not support {1} as content.", parent
, child
));
978 private unsafe Type
LookupType (Value
* top_level
, string assembly_name
, string full_name
)
982 if (assembly_name
!= null) {
984 // if we're given an explicit assembly
985 // name, try and load it, then get the
986 // type from just that assembly
988 Assembly assembly
= null;
989 if (LoadAssembly (assembly_name
, out assembly
) == AssemblyLoadResult
.Success
) {
990 res
= assembly
.GetType (full_name
);
995 Console
.Error
.WriteLine ("unable to load assembly for target type.");
1000 // if we're not given an explicit
1001 // assembly name, loop over all
1002 // assemblies specified in
1003 // Deployment.Parts looking for the
1005 foreach (Assembly a
in Deployment
.Current
.Assemblies
) {
1006 res
= a
.GetType (full_name
);
1011 Assembly assembly
= typeof (DependencyObject
).Assembly
;
1012 res
= assembly
.GetType (full_name
);
1013 if (res
!= null && res
.IsPublic
)
1018 return Application
.GetComponentTypeFromName (full_name
);
1021 private unsafe void SetCLRPropertyFromString (XamlCallbackData
*data
, IntPtr target_data
, object target
, PropertyInfo pi
, string value, out string error
, out IntPtr unmanaged_value
)
1023 unmanaged_value
= IntPtr
.Zero
;
1026 object new_value
= null;
1030 if (IsExplicitNull (value)) {
1031 Type t
= pi
.PropertyType
;
1032 if (t
.IsValueType
&& !(t
.IsGenericType
&& t
.GetGenericTypeDefinition () == typeof (Nullable
<>))) {
1033 error
= "Unable to set non nullable type to null.";
1038 new_value
= MoonlightTypeConverter
.ConvertObject (pi
, value, target
.GetType ());
1045 SetValue (data
, target_data
, pi
, target
, new_value
);
1047 } catch (Exception ex
) {
1054 // lastly, attempt to create an unmanaged Value* object, if one is created, the managed
1055 // parser will create a managed wrapper for the object and call SetPropertyFromValue with
1056 // the managed object
1058 bool result
= NativeMethods
.value_from_str_with_typename (TypeToMoonType (pi
.PropertyType
), pi
.Name
, value, out unmanaged_value
);
1060 error
= string.Format ("unable to convert to type {0} from a string", pi
.PropertyType
);
1064 private string TypeToMoonType (Type t
)
1066 if (t
== typeof (double))
1068 if (t
== typeof (bool))
1074 // TODO: Is it legal to jam the whole metadata right in the string ie: TargetType="clr-namespace:Mono;MyType"
1076 private unsafe Type
TypeFromString (XamlCallbackData
*data
, string str
)
1078 string assembly_name
= null;
1079 string full_name
= str
;
1081 int ps
= str
.IndexOf (':');
1083 string xmlns
= NativeMethods
.xaml_uri_for_prefix (data
->parser
, str
.Substring (0, ps
));
1084 string name
= str
.Substring (ps
+ 1, str
.Length
- ps
-1);
1086 return TypeFromString (data
, xmlns
, name
);
1089 return LookupType (data
->top_level
, assembly_name
, full_name
);
1092 private unsafe Type
TypeFromString (XamlCallbackData
*data
, string xmlns
, string name
)
1094 string clr_namespace
= ClrNamespaceFromXmlns (xmlns
);
1095 string assembly_name
= AssemblyNameFromXmlns (xmlns
);
1097 string full_name
= string.IsNullOrEmpty (clr_namespace
) ? name
: clr_namespace
+ "." + name
;
1099 return LookupType (data
->top_level
, assembly_name
, full_name
);
1102 private unsafe DependencyProperty
DependencyPropertyFromString (XamlCallbackData
*data
, object otarget
, Value
* target_parent_ptr
, string str_value
)
1104 object o
= Value
.ToObject (null, target_parent_ptr
);
1105 Style parent
= o
as Style
;
1107 if (parent
== null) {
1108 Console
.Error
.WriteLine ("DependencyPropertyFromString Parent of target is not a Style. It's a {0}", o
);
1112 Type target_type
= parent
.TargetType
;
1113 if (target_type
== null) {
1114 Console
.Error
.WriteLine ("DependencyPropertyFromString TargetType is null.");
1119 // Check to see if we have an attached property
1121 int dot
= str_value
.IndexOf ('.');
1123 string type_name
= str_value
.Substring (0, dot
);
1124 str_value
= str_value
.Substring (++dot
, str_value
.Length
- dot
);
1126 target_type
= TypeFromString (data
, type_name
);
1129 Types
.Ensure (target_type
);
1131 ManagedType mt
= Deployment
.Current
.Types
.Find (target_type
);
1132 DependencyProperty dp
= DependencyProperty
.Lookup ((Kind
) mt
.native_handle
, str_value
);
1137 private unsafe bool SetPropertyFromValue (XamlCallbackData
*data
, object target
, IntPtr target_data
, Value
* target_parent_ptr
, PropertyInfo pi
, Value
* value_ptr
, IntPtr value_data
, out string error
)
1140 object obj_value
= Value
.ToObject (null, value_ptr
);
1144 if (pi
.GetCustomAttributes (typeof (SetPropertyDelayedAttribute
), true).Length
> 0) {
1145 if ((data
->flags
& XamlCallbackFlags
.SettingDelayedProperty
) == 0) {
1146 Value v
= *value_ptr
;
1147 NativeMethods
.xaml_delay_set_property (data
->parser
, target_data
, null, pi
.Name
, ref v
);
1153 if (obj_value
is Binding
&& target
is FrameworkElement
) {
1154 FrameworkElement fe
= (FrameworkElement
) target
;
1155 fe
.SetBinding (DependencyProperty
.Lookup (fe
.GetKind (), pi
.Name
), (Binding
) obj_value
);
1159 if (obj_value
is StaticResource
) {
1160 StaticResource sr
= (StaticResource
)obj_value
;
1161 obj_value
= "{StaticResource " + sr.ResourceKey + "}";
1164 if (typeof (IList
).IsAssignableFrom (pi
.PropertyType
) && !(obj_value
is IList
)) {
1165 // This case is handled in the AddChild code
1169 if (typeof (ResourceDictionary
).IsAssignableFrom (pi
.PropertyType
) && !(obj_value
is ResourceDictionary
)) {
1170 // This case is handled in the AddChild code
1174 string str_value
= obj_value
as string;
1175 if (str_value
!= null) {
1176 IntPtr unmanaged_value
;
1180 // HACK: This really shouldn't be here, but I don't want to bother putting it in Helper, because that
1181 // code probably should be moved into this file
1183 if (pi
.PropertyType
== typeof (Type
)) {
1184 Type t
= TypeFromString (data
, str_value
);
1186 SetValue (data
, target_data
, pi
, target
, t
);
1191 if (pi
.PropertyType
== typeof (DependencyProperty
)) {
1192 DependencyProperty dp
= DependencyPropertyFromString (data
, target
, target_parent_ptr
, str_value
);
1194 SetValue (data
, target_data
, pi
, target
, dp
);
1199 if (typeof (System
.Windows
.Data
.Binding
).IsAssignableFrom (pi
.PropertyType
) && MarkupExpressionParser
.IsBinding (str_value
)) {
1200 MarkupExpressionParser p
= new MarkupExpressionParser (null, pi
.Name
, data
->parser
, target_data
);
1202 string expression
= str_value
;
1203 obj_value
= p
.ParseExpression (ref expression
);
1205 if (!(obj_value
is Binding
))
1208 SetValue (data
, target_data
, pi
, target
, obj_value
);
1212 if (MarkupExpressionParser
.IsStaticResource (str_value
)) {
1213 // FIXME: The NUnit tests show we need to use the parent of the target to resolve
1214 // the StaticResource, but are there any cases where we should use the actual target?
1215 DependencyObject parent
= Value
.ToObject (null, target_parent_ptr
) as DependencyObject
;
1218 MarkupExpressionParser p
= new MarkupExpressionParser (parent
, "", data
->parser
, target_data
);
1219 obj_value
= p
.ParseExpression (ref str_value
);
1220 obj_value
= ConvertType (pi
, pi
.PropertyType
, obj_value
);
1222 SetValue (data
, target_data
, pi
, target
, obj_value
);
1226 SetCLRPropertyFromString (data
, target_data
, target
, pi
, str_value
, out error
, out unmanaged_value
);
1228 if (error
== null && unmanaged_value
!= IntPtr
.Zero
)
1229 obj_value
= Value
.ToObject (null, unmanaged_value
);
1231 return error
== null;
1233 obj_value
= Value
.ToObject (pi
.PropertyType
, value_ptr
);
1236 obj_value
= ConvertType (pi
, pi
.PropertyType
, obj_value
);
1237 SetValue (data
, target_data
, pi
, target
, obj_value
);
1242 private static unsafe void SetValue (XamlCallbackData
*data
, IntPtr target_data
, PropertyInfo pi
, object target
, object value)
1244 SetterBase sb
= target
as SetterBase
;
1247 sb
.IsSealed
= false;
1250 if (NativeMethods
.xaml_is_property_set (data
->parser
, target_data
, pi
.Name
))
1251 throw new XamlParseException (2033, String
.Format ("Cannot specify the value multiple times for property: {0}.", pi
.Name
));
1253 pi
.SetValue (target
, value, null);
1255 NativeMethods
.xaml_mark_property_as_set (data
->parser
, target_data
, pi
.Name
);
1263 private static object ConvertType (MemberInfo pi
, Type t
, object value)
1268 Type valueType
= value.GetType ();
1274 string str_value
= value as string;
1275 if (str_value
!= null)
1276 return Enum
.Parse (t
, str_value
, true);
1277 if (Enum
.IsDefined (t
, value))
1278 return Enum
.ToObject (t
, value);
1283 TypeConverter converter
= Helper
.GetConverterFor (pi
, t
);
1284 if (converter
== null) {
1286 converter
= new MoonlightTypeConverter (pi
== null ? null : pi
.Name
, t
);
1292 if (converter
!= null && converter
.CanConvertFrom (value.GetType ()))
1293 return converter
.ConvertFrom (value);
1296 if (!valueType
.IsSubclassOf (t
))
1297 value = Convert
.ChangeType (value, t
, System
.Globalization
.CultureInfo
.CurrentCulture
);
1301 // This will just let things fail
1305 private static string ClrNamespaceFromXmlns (string xmlns
)
1307 if (String
.IsNullOrEmpty (xmlns
))
1310 int start
= xmlns
.IndexOf ("clr-namespace:");
1314 start
+= "clr-namespace:".Length
;
1316 int end
= xmlns
.IndexOf (';', start
);
1320 return xmlns
.Substring (start
, end
- start
);
1323 private static string AssemblyNameFromXmlns (string xmlns
)
1325 if (String
.IsNullOrEmpty (xmlns
))
1328 int start
= xmlns
.IndexOf ("assembly=");
1332 start
+= "assembly=".Length
;
1333 int end
= xmlns
.IndexOf (';', start
);
1336 return xmlns
.Substring (start
, end
- start
);
1339 private static bool ValidateXmlns (string xmlns
)
1342 if (Uri
.TryCreate (xmlns
, UriKind
.Absolute
, out dummy
))
1345 int start
= xmlns
.IndexOf ("clr-namespace");
1346 int end
= start
+ "clr-namespace".Length
;
1347 if (end
>= xmlns
.Length
|| xmlns
[end
] != ':')
1350 start
= xmlns
.IndexOf ("assembly");
1352 end
= start
+ "assembly".Length
;
1353 if (end
>= xmlns
.Length
|| xmlns
[end
] != '=')
1360 private unsafe MethodInfo
GetSetMethodForAttachedProperty (Value
*top_level
, string xmlns
, string type_name
, string prop_name
)
1362 string assembly_name
= AssemblyNameFromXmlns (xmlns
);
1363 string ns
= ClrNamespaceFromXmlns (xmlns
);
1365 if (assembly_name
== null && !TryGetDefaultAssemblyName (top_level
, out assembly_name
)) {
1366 Console
.Error
.WriteLine ("Unable to find an assembly to load type from.");
1371 if (LoadAssembly (assembly_name
, out clientlib
) != AssemblyLoadResult
.Success
) {
1372 Console
.Error
.WriteLine ("couldn't load assembly: {0} namespace: {1}", assembly_name
, ns
);
1376 Type attach_type
= clientlib
.GetType (type_name
, false);
1377 if (attach_type
== null) {
1378 attach_type
= Application
.GetComponentTypeFromName (type_name
);
1379 if (attach_type
== null) {
1380 Console
.Error
.WriteLine ("attach type is null {0} '{1}'", type_name
, ns
);
1385 MethodInfo set_method
= attach_type
.GetMethod (String
.Concat ("Set", prop_name
), BindingFlags
.Static
| BindingFlags
.Public
| BindingFlags
.NonPublic
);
1389 private static unsafe object GetObjectValue (object target
, IntPtr target_data
, string prop_name
, IntPtr parser
, Value
* value_ptr
, out string error
)
1393 IntPtr unmanaged_value
= IntPtr
.Zero
;
1394 object o_value
= Value
.ToObject (null, value_ptr
);
1395 if (error
== null && unmanaged_value
!= IntPtr
.Zero
)
1396 o_value
= Value
.ToObject (null, unmanaged_value
);
1398 if (o_value
is String
&& MarkupExpressionParser
.IsStaticResource ((string) o_value
)) {
1399 MarkupExpressionParser mp
= new MarkupExpressionParser ((DependencyObject
) target
, prop_name
, parser
, target_data
);
1400 string str_value
= o_value
as String
;
1401 o_value
= mp
.ParseExpression (ref str_value
);
1407 private static bool IsExplicitNull (string value)
1409 return Regex
.IsMatch (value, "^{\\s*x:Null\\s*}");
1412 private PropertyInfo
GetContentProperty (Type t
)
1415 string content_property
= null;
1417 while (walk
!= null) {
1418 content_property
= GetContentPropertyNameForType (walk
);
1419 if (content_property
!= null)
1421 walk
= walk
.BaseType
;
1424 if (walk
== null || content_property
== null)
1427 PropertyInfo pi
= walk
.GetProperty (content_property
, BindingFlags
.Instance
| BindingFlags
.NonPublic
| BindingFlags
.Public
| BindingFlags
.FlattenHierarchy
);
1431 private string GetContentPropertyNameForType (Type t
)
1433 object [] o
= t
.GetCustomAttributes (typeof (ContentPropertyAttribute
), false);
1438 ContentPropertyAttribute cpa
= (ContentPropertyAttribute
) o
[0];
1442 private string GetContentPropertyName (Type t
)
1444 object [] o
= t
.GetCustomAttributes (typeof (ContentPropertyAttribute
), true);
1447 ContentPropertyAttribute cpa
= (ContentPropertyAttribute
) o
[0];
1454 /// Callbacks invoked by the xaml.cpp C++ parser
1458 #region Callbacks from xaml.cpp
1460 // Proxy so that we return IntPtr.Zero in case of any failures, instead of
1461 // genereting an exception and unwinding the stack.
1463 private unsafe bool cb_lookup_object (XamlCallbackData
*data
, Value
* parent
, string xmlns
, string name
, bool create
, bool is_property
, out Value
value, ref MoonError error
)
1466 return LookupObject (data
->top_level
, parent
, xmlns
, name
, create
, is_property
, out value);
1467 } catch (Exception ex
) {
1468 Console
.Error
.WriteLine ("ManagedXamlLoader::LookupObject ({0}, {1}, {2}, {3}) failed: {3} ({4}).", (IntPtr
) data
->top_level
, xmlns
, create
, name
, ex
.Message
, ex
.GetType ().FullName
);
1469 Console
.WriteLine (ex
);
1470 value = Value
.Empty
;
1471 error
= new MoonError (ex
);
1476 private void cb_create_gchandle ()
1478 if (!handle
.IsAllocated
)
1479 handle
= GCHandle
.Alloc (this);
1483 // Proxy so that we return IntPtr.Zero in case of any failures, instead of
1484 // generating an exception and unwinding the stack.
1486 private unsafe bool cb_set_property (XamlCallbackData
*data
, string xmlns
, Value
* target
, IntPtr target_data
, Value
* target_parent
, string prop_xmlns
, string name
, Value
* value_ptr
, IntPtr value_data
, ref MoonError error
)
1489 return SetProperty (data
, xmlns
, target
, target_data
, target_parent
, prop_xmlns
, name
, value_ptr
, value_data
);
1490 } catch (Exception ex
) {
1491 Console
.Error
.WriteLine ("ManagedXamlLoader::SetProperty ({0}, {1}, {2}, {3}, {4}) threw an exception: {5}.", (IntPtr
) data
->top_level
, xmlns
, (IntPtr
)target
, name
, (IntPtr
)value_ptr
, ex
.Message
);
1492 Console
.Error
.WriteLine (ex
);
1493 error
= new MoonError (ex
);
1498 private unsafe bool cb_import_xaml_xmlns (XamlCallbackData
*data
, string xmlns
, ref MoonError error
)
1501 if (!ValidateXmlns (xmlns
))
1503 Application
.ImportXamlNamespace (xmlns
);
1505 } catch (Exception ex
) {
1506 Console
.WriteLine ("Application::ImportXamlNamespace ({0}) threw an exception:\n{1}", xmlns
, ex
);
1507 error
= new MoonError (ex
);
1513 private unsafe string cb_get_content_property_name (XamlCallbackData
*data
, Value
* object_ptr
, ref MoonError error
)
1515 object obj
= Value
.ToObject (null, object_ptr
);
1520 Type t
= obj
.GetType ();
1521 return GetContentPropertyName (t
);
1525 private unsafe bool cb_add_child (XamlCallbackData
*data
, Value
* parent_parent
, bool parent_is_property
, string parent_xmlns
, Value
*parent
, IntPtr parent_data
, Value
* child
, IntPtr child_data
, ref MoonError error
)
1528 return AddChild (data
, parent_parent
, parent_is_property
, parent_xmlns
, parent
, parent_data
, child
, child_data
);
1529 } catch (Exception ex
) {
1530 Console
.Error
.WriteLine (ex
);
1531 error
= new MoonError (ex
);