Resolve generic types with IXamlTypeResolver - wpf

I'd written a new TypeExtension class that replace the default System.Windows.Markup.TypeExtension to handle generic types. It allows the Xaml to work with generic types in many different ways, like :
<DataTemplate DataType="{ck:Type [here a generic type name]}">
This is how it works :
I'd written TypeExtention class, that inherit of MarkupExtension (it's the ck:Type in the example)
I'd override the ProvideValue method to call Resolve on IXamlTypeResolver
IXamlTypeResolver service = p.GetService( true );
_type = service.Resolve( _typeName );
So, in .NET 3.5, I can resolve types like "local:IConfigItemProperty`1". But now in .NET 4 (with WPF 4) the resolve method throws an exception :
Character '' was unexpected in string 'local:IConfigItemProperty1'. Invalid XAML type name.
Do you think .NET 4.0 does not support '`' anymore ?

While the post is a bit old, i put the answer for any other dev looking for solution.
It's seem from 4.0 they change the way the Reader deal with generic. The lack of support to '`' by the IXamlTypeResolver service seem to be a bug while the regression is not documented at all.
If u want to use generic, you might want to switch with x:TypeArguments which is list the argument for the generic.
C#
public class GraphBase<N,L,P> : IGraph<N,L,P>
{
...
}
XAML
<?xml version="1.0" encoding="utf-8" ?>
<GraphBase
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns="clr-namespace:MyGeoLive.Topology;assembly=MyGeoLive.Topology"
xmlns:System="clr-namespace:System;assembly=mscorlib"
x:TypeArguments="System:String,System:String,System:String" >
</GraphBase>

Related

WPF two-way binding with internal setter

I'm using WPF's two-way binding on a CLR property, which implements INotifyPropertyChanged.
The set for the property is internal, while the get is public.
Unfortunately, I get the following error:
System.Windows.Markup.XamlParseException was unhandled
Message: An unhandled exception of type 'System.Windows.Markup.XamlParseException' occurred in PresentationFramework.dll
Additional information: A TwoWay or OneWayToSource binding cannot work on the read-only property 'Name' of type 'MyType'.
Is this the expected behavior? I would have thought that internal setters should work just fine...
Note that the CLR-type is defined in another assembly, and are visible in the current assembly, with the [assembly: InternalsVisibleTo("MyAssembly")] attribute.
Does anyone have workarounds/suggestions? The declaring assembly is a class library, so it's not an option for me to change the set to public.
You can create your own NEW public wraper property and use getter and setter of it to interact with your internal property
internal string _SideTabHeader;
public string SideTabHeader
{
get { return _SideTabHeader; }
set
{
if( value<0)
{
do nothing
}
else
{
_SideTabHeader=value;
};
}
}
Oh my... I just found out, WPF bindings don't work with internal properties. Oh, Microsoft... Whatever were you thinking?
Update:
Here's what I've understood so far (Thank you, #Grx70):
WPF is not a native part of the .NET framework, it's just a "plug-in" framework that happens to be also written by Microsoft. That is why it can't access the internal members of your assembly.
Microsoft could have allowed WPF to respect the [assembly: InternalsVisibleTo("XXX")] attribute, but as of right now, WPF ignores it - which unfortunately does not leave one with any easy workarounds.
Note: I tested using InternalVisibleTo - both Signed and Unsigned, with PresentationFramework, PresentationCore, and a whole bunch of other DLLs with no luck.
The only workaround I can think of right now is to create a "Proxy" class which can expose all required members as public. This is quite a PITA (I have a LOT of classes, and I hate the maintenance nightmare that comes with creating an equal number of "Proxy" classes) - so I might look into using PostSharp, or Fody or some kind of weaver to auto-create these "Proxy" classes if I can.
All the best to anyone else facing this issue.
This is very late and not solving the initial question, but as very related it may help someone else which very similar problem...
If your internal property is of type Enum else skip
In my case I was trying to do a WPF xaml binding to a property of type inherited from a WCF service. The easy way to solve that simple case was to use int.
public Dictionary<int, string> ProductsList => EnumExtensions.ProductsList;
public int ProductType
{
get { return (int)_DeliveryProduct.ProductType; }
set
{
if (value.Equals(ProductType)) return;
_DeliveryProduct.ProductType = (ProductEnum)value;
RaisePropertyChanged(() => ProductType);
}
}
_DeliveryProduct is my reference to my domain object for which the property ProductType is an enum but in my viewmodel that property is an int.
... Note that ProductEnum is autogenerated from the API and can't be changed to public.
internal static Dictionary<int, string> ProductsList => new Dictionary<int, string>
{
{(int)ProductEnum.Regular, ProductEnum.Regular.GetDisplayName()},
{(int)ProductEnum.Intermediate, ProductEnum.Intermediate.GetDisplayName()},
{(int)ProductEnum.Super, ProductEnum.Super.GetDisplayName()},
{(int)ProductEnum.Diesel, ProductEnum.Diesel.GetDisplayName()}
};

Why Can't I Use My DbContext Type?

I'd like to access some static properties of my DbContext type in a WPF Window. I thought I could use the same XAML that I use to refer to individual entities:
<Window.Resources>
<entity:Account x:Key="account"/> //Works fine
<entity:MyEntities x:Key="myEntities"/> //Throws an error!
</Window.Resources>
I get this error:
No connection string named 'MyEntities' could be found in the application config file.
Why is it treating the DbContext type (MyEntities) differently than the Account entity? Is there an easy way I can access the static properties of my MyEntities type?
The syntax you used is for creating instances, not static properties. If you want to access a static property you need to use the x:Static markup extension
<Window.Resources>
<entity:Account x:Key="account" SomeProperty={x:Static entity:MyEntities.MyProperty}/>
</Window.Resources>
The above xaml would be similar to the C# code
var account = new Account()
{
SomeProperty = MyEntities.MyProperty
};
this.Resources["account"] = account;
See that you are calling new Account(), if you called new MyEntites() (like your original example did) you get the error you where getting.
It appears that particular error results due to the static constructor that I placed in my DbContext. When I remove the static constructor the error changes to:
Object reference not set to an instance of an object.
As it turns out, the original error doesn't prevent me from compiling or running my application. I changed my code to use Scott Chamberlain's suggestion (which produces a similar ignorable error) because it is much cleaner and I can access the static properties on the DbContext just fine in spite of Visual Studio's complaints. Thanks, everyone, for the help and suggestions.

Design Time - Dynamic

can i create a view at design time, using dynamic objects? (with visual studio 2010)
for example, or something, is that possible?
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:dyn="clr-namespace:System.Dynamic;assembly=System.Core"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
and
<d:DesignProperties.DataContext>
<dyn:ExpandoObject>
<dyn:ExpandoObject.Name>MyName</dyn:ExpandoObject.Name>
</dyn:ExpandoObject>
</d:DesignProperties.DataContext>
The above example does not work.
Sometimes I have class complicated to manage. And I can not use them at design time, the idea of using a dynamic type would be not to change the bindings (properties that I will use) and get a vision.
I do not know if I was clear, but if you have something that can help me would be great.
That should not work in any case, be it design or run-time.
You can use ExpandoObjects, but only via dictionary syntax:
<dyn:ExpandoObject>
<sys:String x:Key="Name">MyName</sys:String>
</dyn:ExpandoObject>
Though i do not know if that junk of a GUI designer is able to handle that, nor do i know if this works with compiled XAML.
Edit: As i thought you cannot compile this because some component fails:
Error 1 The Key attribute can only be used on a tag contained in a Dictionary (such as a ResourceDictionary).
Well, it is a bloody dictionary alright...
You can use a markup extension though, you can get the DictionaryFactoryExtension from this answer and modify it like this:
public override object ProvideValue(IServiceProvider serviceProvider)
{
var expando = (IDictionary<string,object>)new ExpandoObject();
foreach (DictionaryEntry kvp in Dictionary)
expando[(string)kvp.Key] = kvp.Value;
return expando;
}
//The designer uses this for whatever reason...
public object this[object key]
{
get { return this.Dictionary[key]; }
set { this.Dictionary[key] = value; }
}
Then you can use that just like an ExpandoObject:
<!-- Of course you can also hard-code the key-type in the class,
it should be a string in all cases when using an ExpandoObject anyway -->
<local:DictionaryFactory KeyType="sys:String" ValueType="sys:Object">
<sys:String x:Key="Name">MyName</sys:String>
</local:DictionaryFactory>
The designer should be able to handle this.

MC3074 - type does not exist in "clr-namespace..."

Im having trouble referencing classes in xaml from other assemblies.
In the same solution, i have two projects. One called Controls (to hold user controls) and one called DataBinding (holding converters / validation rules). In a control, im attempting reference a validation rule in xaml:
<Binding.ValidationRules>
<databind:Validators.FileExistsRule />
</Binding.ValidationRules>
My project references the project containing my classes. Ive added this declaration at the top of my Control.xaml:
xmlns:databind="clr-namespace:GuiParts.DataBinding;assembly=DataBinding"
However, when i compile, i get an error:
The tag 'Validators.FileExistsRule' does not exist in XML namespace 'clr-namespace:GuiParts.DataBinding;assembly=DataBinding'.
The class definitely exists, i can call it in the code behind with no problems, but not via xaml. If i move the class to the same project, again, i have no problems. Ive seen other questions on here, and have tried the following:
Cleaning and rebuilding all relevant projects
Ensuring all projects are targeting the same version of .Net (4.0, Full Profile)
Removing the 'assembly' definition from the end of the namespace definition.
None of the above has worked. Any suggestions as to where im going wrong?
EDIT
My FileExists Validator:
namespace GuiParts.DataBinding.Validators
{
/// <summary>
/// Validates that the file with the specified name exists
/// </summary>
public class FileExistsRule : ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
ValidationResult res = null;
res = ( ! File.Exists((string)value))
? new ValidationResult(false, "File does not exist")
: new ValidationResult(true, null);
return res;
}
}
}
I can call the following in the code behind without any errors:
new GuiParts.DataBinding.Validators.FileExistsRule();
So ive got my namespaces etc. correct.
Try this:
xmlns:databind="clr-namespace:GuiParts.DataBinding.Validators;assembly=DataBinding"
<Binding.ValidationRules>
<databind:FileExistsRule />
</Binding.ValidationRules>
Is your class in your target assembly public?
Is the field in Validators, public?
Is your namespace GuiParts.DataBinding correct?
While I'm not sure what the issue you're having is, you can alternatively create a friendlier namespace definition for your assembly and CLR namespaces. In fact, I use this technique to group various namespaces into one XML namespace... You do this using the XmlnsPrefixAttribute and the XmlnsDefinitionAttributes.
For example:
[assembly: XmlnsPrefix("http://my.xml.namespace.com/", "databind")]
[assembly: XmlnsDefinition("http://my.xml.namespace.com/",
"GuiParts.DataBinding")]
[assembly: XmlnsDefinition("http://my.xml.namespace.com/",
"GuiParts.DataBinding.Validators")]
Then, when you want to reference the xmlnamespace in your xaml you simply do:
xmlns:databind="http://my.xml.namespace.com/"
Note, I use ReSharper, but I'm sure this also works naturally in Visual Studio. If you don't type in the xmlns import and attempt to use an object in the namespace, then when you go to resolve it it will automatically use the friendlier named namespace with the specified prefix. Also, it's really nice and helps you avoid having a 1:1 relationship between xml namespaces and clr namespaces because you can map multiple clr namespaces into a single xml namespace.
Again, I'm not sure what the issue you're having specifically is, but this will probably fix it and be better than using clr-namespaces and assembly information. Just make sure to come up with something unique so you don't run into xmlns collisions or you'll have to go back to clr/assembly namespacing.
Oh, and one last thing... if you wish to utilize versioning in your xml namespace naming scheme (which you should), don't worry about locking yourself in for backwards compatibility. You can always utilize the XmlnsCompatibleWithAttribute to ensure that code that utilized the old friendly namespace doesn't break if you ever update your external assemblies to map to a newer xml namespace.
For example, if you originally had you're assembly pointing to a 2012 namespace, then switched it to a 2013 namespace because you updated the assembly...
// Previous Assembly version
//[assembly: XmlnsDefinition("http://schemas.xyzcorp.com/wpf/2012",
// "Xyz.Databinding")]
[assembly: XmlnsCompatibleWith("http://schemas.xyzcorp.com/wpf/2012",
"http://schemas.xyzcorp.com/wpf/2013")]
[assembly: XmlnsDefinition("http://schemas.xyzcorp.com/wpf/2013",
"Xyz.Databinding")]

Embedded IronPython scripts and converting types

I've got a WPF application that embeds IronPython to use as a scripting language. I've got an object model that IronPython scripts can use to do 'stuff'.
However I've come across a strange problem that I've solved in a way that I don't believe is correct.
In my script I want to type the following to set the location of an object in WPF.
map.CanvasLocation = 10,10
This comes up with an exception saying that it cannot convert from PythonTuple to System.Windows.Point.
I've currently solved that using a custom type converter in my c# object, but I'm not sure if this is the best way to do it.
Is there a way to tell IronPython or .Net in general how to convert from one type to another that can be extended at run time?
The way I do this is to use TypeDescriptor to add a type converter attribute to PythonTuple at runtime.
TypeDescriptor.AddAttributes(typeof(PythonTuple),
new TypeConverterAttribute(typeof(Converter)));
Then I use the following code to find the converter in the attribute setter (SetMemberAfter method)
var converter = TypeDescriptor.GetConverter(value);
if (converter.CanConvertTo(destinationType))
{
var destinationValue = converter.ConvertTo(value, destinationType);
return destinationValue;
}
else
{
throw new InvalidOperationException("Cannot convert from {0} to {1}".UIFormat(
value == null ? "null" : value.GetType().Name, destinationType.Name));
}
Why not do a
map.CanvasLocation = Point(10,10)

Resources