WPF Generic Windows - wpf

I want to make a reusable WPF Window suitable for different types T.
I have a designer and a codebehind file.
can I do something like this?
/* Code behind file */
public partial class MyWindows<T> : Window
{}

Shamelessly copied from here (and thus not tested)
public class ViewBase<T> : Window, IView where T : class, IViewModel
{
public virtual T Model
{
get { return DataContext as T; }
set { DataContext = value; }
}
}
and XAML
<src:ViewBase
x:Class="View"
x:TypeArguments="src:IViewModel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="clr-namespace:MyNamespace"
Height="480" Width="640">
...
</src:ViewBase>

Unfortunately, what you want isn't quite possible.
Update: Before .NET 4.0 (i.e. when this answer was originally written), XAML support for consuming generic types was very limited; e.g. generics only worked on the root element. In .NET 4.0, some restrictions were lifted.
In .NET 4.0, you can construct a fully specialized generic type. So while XAML itself still has no concept of generic types, it can refer to specializations of generic types. (By analogy, XAML can't express the notion List<> but it can express the notion List<int>). For full details, see the MSDN page "Generics in XAML".
You can construct instances of specialized generic types with the x:TypeArguments Directive. For example, with x bound to XAML's namespace, sys to the System namespace, and scg to System.Collections.Generic, and your own MyWindows' namespace bound to my then:
<my:MyWindows x:TypeArguments="x:String"> would construct a MyWindows<string> instance.
<scg:List x:TypeArguments="sys:Tuple(sys:String,sys:Int32)"> would construct a List<Tuple<string,int>>
Using generic types is therefore no longer a problem in XAML!
Alas, you want to define a generic type in XAML. That's not possible. There are two workarounds here. Firstly (and based on your comments on another question I think this is what you want) you can simple pass a type as a plain parameter. If you do this, you lose all the compile-time safety features that generics provide, but often enough those aren't relevant. Secondly, you can define a normal non-generic class with codebehind in XAML, and simply use a generic base class for code reuse. That way you get at least some proper generics safety and reuse.

Related

Using MvxVisibilityValueConverter in WPF

I'm just getting started with MVVMCross, so forgive me if this seems like a simple question. I'm trying use the MVVMCross Visibility plugin in WPF, mentioned here:
https://github.com/MvvmCross/MvvmCross/wiki/Value-Converters
I installed the plugin, and am trying to follow these steps:
Windows - use Native wrappers or Tibet Binding as described above:
Visibility="{Binding VMProperty, Converter={StaticResource
Visibility}}"
When I try to do so, it can't find the resource "Visibility."
So I figured, I can add the namespace:
xmlns:visibility="clr-namespace:Cirrious.MvvmCross.Plugins.Visibility;assembly=Cirrious.MvvmCross.Plugins.Visibility"
...and then add the converter to my resources:
<visibility:MvxVisibilityValueConverter x:Key="Visibility"></visibility:MvxVisibilityValueConverter>
...but now I get:
An object of the type "Cirrious.MvvmCross.Plugins.Visibility.MvxVisibilityValueConverter" cannot be applied to a property that expects the type "System.Windows.Data.IValueConverter".
Do I have to make my own Converter for this, like this:
class MyVisibilityConverter : MvxNativeValueConverter<MvxVisibilityValueConverter>
{
}
...or am I missing something? The docs seem to indicate there's less work involved.
IValueConverter isn't currently a portable interface, and this was a deliberate decision from Microsoft. I've talked to one of the guys from the PCL team about this - he seemed very clear that they expected most value converters to be platform specific and so not to sit in shared code.
Because of this - and because MvvmCross believes many value converters will be shared - we had to introduce our own IMvxValueConverter interface inside MvvmCross. This IMvx interface can't be used directly by XAML and the Microsoft bindings - so that's the reason you need the "native" wrapping currently.
You can work around this - if you want to - by using the MvvmCross "Tibet" binding framework instead of the Microsoft one, but I think most MS-based devs are still using the MS-binding.
am I missing something? The docs seem to indicate there's less work involved.
For using value converters on Windows, the wiki says the text below - if you think this can be improved, please do contribute changes back - we're keen to keep on improving.
Using Value Converters in Windows (conventional Xaml binding)
The IMvxValueConverter interface is closely based on the IValueConverter interface used in Windows WPF and Silverlight Xaml binding. This interface is also similar (but slightly different) to the IValueConverter interface used in Windows WinRT Xaml binding.
Because these Xaml IValueConverter interfaces are not 100% identical to each other, nor to the IMvxValueConverter version, shared Mvx ValueConverters cannot be used directly in Windows Xaml binding - they must instead be wrapped for use in Xaml.
The steps to do this are similar on each Windows platform:
for each IMvxValueConverter class, e.g. for
public class TheTruthValueConverter
: MvxValueConverter<bool, string>
{
public string Convert(bool value, Type targetType, CultureInfo cultureInfo, object parameter)
{
return value ? "Yay" : "Nay";
}
}
in your UI project, create a 'native' wrapper using the MvxNativeValueConverter class:
public class TheNativeTruthValueConverter
: MvxNativeValueConverter<TheTruthValueConverter>
{
}
in your Xaml, include an instance of your ValueConverter as a static resource - this can be done in the Resources at App, Page or Control Xaml level, e.g.:
<converters:TheNativeTruthValueConverter x:Key="TheTruth" />
now your converter can be used - e.g.:
<TextBlock Text="{Binding HasAccepted, Converter={StaticResource TheTruth}}" />
Using Value Converters in Windows (Tibet binding)
In addition to 'traditional' Xaml bindings, MvvmCross also allows 'Tibet' binding within Windows - for more on this see wiki/Databinding.
When Tibet binding is used, then Value Converters can be accessed by name - exactly as in Droid and Touch binding - without the above native Xaml wrapping.
Further, if using 'Tibet' binding then an entire assembly's worth of value converters can be registered using the Reflection sweep technique and this can be specified at the Xaml level - meaning it can be used in both design and run-time.
To include all value converters within an Assembly at the Xaml level, then use an mvx:Import block with an inner From attribute which contains an instance of a class from that Assembly.
This may sound complicated… but actually it is quite simple.
Suppose you have an Assembly MyTools containing FooValueConverter, BarValueConverter, etc
Within this Assembly add a simple, instanciable public Class which we will use only for the import - e.g. public class MarkerClass {}
Then within the xaml, you can include a static resource import block like:
<mvx:Import x:Key="MvxAssemblyImport0">
<mvx:Import.From>
<myTools:MarkerClass />
<mvx:Import.From>
</mvx:Import>
After this is done, then the ValueConverters Foo and Bar will be available for use within 'Tibet' bindings - e.g. as:
<TextBlock mvx:Bi.nd="Text Foo(Name)" />

How to specify generic type argument in XAML

I have a BaseView for my MVP - PRISM WPF application. Now for some reason we thought to make the _presenter as a Templated field in the BaseView.
earlier i had the view xaml representation as
<base:BaseView xamlns:base="clr address of the dll which had BaseView" >
</base:BaseView>
now since i have changed the BaseView to BaseView<TPresenter>, So how shall i write the Xaml then?
You can do it since .NET 4 Framework and XAML 2009.
See Generics in XAML on MSDN
For instance:
<my:BusinessObject x:TypeArguments="x:String,x:Int32"/>
For .NET 3.5:
For XAML 2006 usage when specifically targeting WPF, x:Class must also
be provided on the same element as x:TypeArguments, and that element
must be the root element in a XAML document. The root element must map
to a generic type with at least one type argument. An example is
PageFunction.
Possible workarounds to support generic usages include defining a
custom markup extension that can return generic types, or providing a
wrapping class definition that derives from a generic type but
flattens the generic constraint in its own class definition.
In case this happens to someone. I had a similar scenario where I converted my base class to a templated class (i.e. BaseView to BaseView). I kept receiving errors in the InitializeComponent() method. I was receiving the null exception error in the x:Class base type's InitializeComponent() call. I was able to resolve the errors by removing the form-level events from the XAML definition. If I had to keep the form-level events I would need to move them to BaseView.

Silverlight define custom Dictionary in XAML

You can define custom Dictionaries in XAML in the following way.
// *.cs
public class MyDictionary : Dictionary<string, int> { }
// *.xaml
<sys:Int32 x:Key="Zero">0</sys:Int32>
But what about other Key-Types? You can use the extended element usage in XAML 2009:
<object>
<x:Key>keyObject</x:Key>
</object>
However, this isn't supported in Silverlight yet.
Is there anything I can do? I want to use a custom Dictionary with System.Type as Key-Type.
Silverlight does not support generics in XAML. You also can't create an arbitrary typed object directly because SL would not know how to instantiate it.
You should consider Chris Haines's suggestion of having your dictionary as a view model because it's better practice to have such things be defined outside of xaml.
You can't really do this in Silverlight (see Is there any way to instantiate a 'Type' in Silverlight XAML?)
You could wrap your dictionary and input strings and let it convert them to types internally, but REALLY, why can't you just use the string representation of Types? Do Type.FullName

Passing large sets of data into user controls

Since UserControls in WPF have to have parameterless constructors, what is the correct way for supplying them with fairly complex data that is needed "near" the time of construction. I have tried using dependency properties for this, but am running into issues with the Visual Studio designer balking at attempts to pass stuff like a Dictionary<string,MyObject> into an IDictionary<string,MyObject> typed dependency property. At some point it must want an exact compile time type match, or the XAML doesn't come up in the designer, although the application executes just fine.
Basically, I want a good way to pass in stuff that I would normally pass into a constructor into a User Control. What's the best way?
Update:
The user control in question will always be created from XAML, so having a non-parameterless construction in addition to the parameterless one is not an option.
Update 2:
An interesting idea would be to have something accessible from the parameterless constructor that I can get my initialization data from. Something like perhaps asking the question: Which of my already initialized ancestors implements an IMyDataProvider interface? This could be similar to how the relative source to ancestor type bindings work, except done programatically from the user control constructor.
If the only problem you are having is passing in derived types, you can pass in instead a simple concrete container class containing your complex types as properties. For example:
public class InitializationData
{
public IDictionary<TKey, TValue> Dictionary { get; set; }
}
This level of indirection will overcome the limitations of the Visual Studio designer.
A couple of options.
1, You can have more than one constructor, a parameterless one for when your control is created via XAML and another that takes a set of parameters for when you create it directly via code. If you definitely don't want to create your instance via code then...
2, Add a public property that only has a setter and is defined with the exact dictionary type you want to pass in and use as the data for initializing the control. The property only needs to be called once. You can have other properties that are getters/setters that expose that initialized data in order more generic types.

how to reference a class defined inside the same page in silverlight

for example:
I have:
public class MyPage : XXXPage
{
.....
public class HahaConverter: IValueConverter
{
.........
....
}
}
In my xaml, can I do this:
<Page.Resources>
<????:HahaConverter :Key="dateConverter" />
<Page.Resources>
I just wondering how to get ????. I could not reference myself?
Thanks
Nested classes will require a fully qualified reference, so you will need to add another xmlns entry to the xaml referencing your parent class namespace. e.g.
xmlns:ValueConverters="clr-namespace:YourAppName.MyPage;assembly=YourAppName"
In VS 2010 once you start typing auto-complete/intellisense should start listing your project namespaces*.
Then reference your value converter with
<ValueConverters:HahaConverter x:Key="dateConverter">
*Note: It is usually recomended to stick to the one-class one-file standard as tools, coders (and VS) cope better. You will likely wind up with a large collection of Value Converters for Silverlight, might as well start a library for them :)

Resources