-
Notifications
You must be signed in to change notification settings - Fork 693
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposal: Generic types in WinUI XAML #931
Comments
This is a great request, one that I've talked to @MikeHillberg about in the past. I think the hangup is that it's not clear what the syntax for it should be in XAML markup. |
I think it's a good idea. Xaml markup mostly aligns with WinRT, which doesn't generally support generic (parameterized) types. But I don't see a problem with the markup language understanding the places where C# has additional features. |
This feature is really essential when using ReactiveUI. @jevansaks, why not same as in the WPF XAML? |
Does this proposal also apply for Simple exampleModel: public class Box<T>
{
public T Value { get; set; }
} XAML: xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:types="using:MySampleTypes"
<DataTemplate x:DataType="types:Box" x:TypeArguments="x:String">
<TextBlock Text="{x:Bind Value}"/>
</DataTemplate> One with also function bindingsModel: public class Box<T>
{
public T Value { get; set; }
} Function: public static class Formatters
{
public static string FancyFormat(int n) => $"FooBar{n}";
} XAML: xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:types="using:MySampleTypes"
xmlns:functions="using:MySampleFunctions"
<DataTemplate x:DataType="types:Box" x:TypeArguments="x:Int32">
<TextBlock Text="{x:Bind functions:Formatters.FancyFormat(Value)}"/>
</DataTemplate> The workaround to this for now is to always declare an empty class that is just an extension of the generic type, with specific strongly typed arguments. This is not ideal though, for two reasons:
Another possible workaround is to expose the generic parameters through an additional
P. S. On a related note to the second example, it'd also be nice to have all the primitive types available from the |
For x:DataType we could also use parens to get the language keyword into one attribute:
The confusing thing about the following is that it looks like it's instantiating a DataTemplate<string>
|
@MikeHillberg That syntax with parents looks great to me! 😄 I just have a question regarding the difference in the syntax for the type arguments in data templates compared to other cases (eg. the If that was possible, we'd end up with the following syntax (again with the code from the first post): <rxui:ReactivePage
x:Class="v:MainPage(vm:MainPageViewModel)"
xmlns:rxui="using:ReactiveUI"
xmlns:v="using:MyProject.Views"
xmlns:vm="using:MyProject.ViewModels" /> I personally like that very much, I'm looking forward to seeing how things move forward with this! 😊 |
Good question. I think the spec on x:TypeArguments is that it closes the open type indicated by the property element tag (<ReactivePage>), which is what we're doing in the x:Class case. Put another way, use x:TypeArguments to close ReactivePage whether you're creating one or deriving from one. (I never loved the x:TypeArguments name. It's exactly the right technical term, but I thought x:Of would be easier to understand.) |
Oh I see now, I was reading that wrong 😄 The distinction between |
@MikeHillberg Does it make sense to use parenthesis which could get confused for functions or use something like curly braces? <DataTemplate x:DataType="types:Box{x:String}">
<TextBlock Text="{x:Bind Value}"/>
</DataTemplate> This would align with xmldoc usage and be more familiar and distinct? /// <summary>
/// An observable group.
/// It associates a <see cref="Key"/> to an <see cref="ObservableCollection{T}"/>.
/// </summary>
/// <typeparam name="TKey">The type of the group key.</typeparam>
/// <typeparam name="TValue">The type of the items in the collection.</typeparam> |
That looks good to me. Nothing seems to be perfect, the curly braces look a little like a markup extension (but it's OK because this isn't in column zero), and square brackets looks like an array reference. |
@MikeHillberg posting here since it's still relevant for generic types in XAML in general, would this issue also cover the proper overload resolution of generic methods used in For instance, suppose I have this property in my viewmodel: public Task<string> MyTask { get; set; } = Task.FromResult("Hello world!"); And then I have this extensions class: public static class TaskExtensions
{
public static object GetResultOrDefault(this Task task)
{
throw new NotImplementedException("Use the generic overload");
}
public static T GetResultOrDefault<T>(this Task<T> task)
{
return task.IsCompletedSuccessfully ? task.Result : default;
}
} Now suppose I try to bind to that <TextBlock
xmlns:ex="using:Extensions"
Text="{x:Bind ex:TaskExtensions.GetResultOrDefault(ViewModel.MyTask)}"/> This will work, but the XAML compiler will create this in global::System.Threading.Tasks.Task<global::System.String> p0;
if (!TryGet_ViewModel_MyTask(out p0)) { return; }
global::System.Object result = global::Extensions.TaskExtensions.GetResultOrDefault(p0); This is because it'll look up the overload just by literally picking the first one it finds (which is the non-generic one), then it'll declare the return type based of that (
It'd be nice if the XAML compiler was also update to fix this issue. Thanks again for your time! EDIT: just noticed that I can also just add those two methods as instance methods in the viewmodel class, and invoke them directly (since function binding supports multiple paths) like so: <TextBlock Text="{x:Bind ViewModel.GetResultOrDefault(ViewModel.MyTask)}"/> Easier to write as there's no need to also declare the |
var result = global::Extensions.TaskExtensions.GetResultOrDefault(p0); Solved. How the CS compiler will see that |
@sharpninja Yes, I know that, I've mentioned that in my post as well:
But this is not a "solution" as it still has a number of problems:
So I wouldn't really say that's "solved" until the XAML compiler gets an update to support this scenario. This is just a workaround, which unfortunately still has a number of downsides. |
This looks worth a separate issue. Changing MyTask to be Task<int> rather than Task<string> makes it more obvious by causing a crash on the type cast. |
It's also useful for a simple scenario such as using |
Want to see this implemented |
+1, right now I am just making an inherited class for each type I need, would be nice to use x:TypeArguments instead |
Any chance this could also support C++ templates? |
Proposal: Enable generic types in UWP XAML
Currently it's impossible to inherit from a generic type and specify generic type arguments in UWP XAML.
Summary
Elsewhere / API:
XAML:
Code behind:
Rationale
The WPF XAML supports the
TypeArguments
XAML directive. My request here is to support this functionality in UWP XAML as well.P.S. Imigrated from here
The text was updated successfully, but these errors were encountered: