I am trying to add a generic Type to my XAML .cs file so it can be re-used with another classes/types. This is where I got so far:
public class PageElementRenderer<T> : PageFunction<T>
{
public PageElementRenderer()
{
}
}
.cs behind XAML:
public partial class PageElementRenderer_Derived : PageElementRenderer<SomeClass>
{
public PageElementRenderer_Derived() : base()
{
}
}
XAML:
<local:PageElementRenderer
x:Class="Application.PageElementRenderer_Derived"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Application">
I get an error:
Using the generic type 'PageElementRenderer' requires 1 type arguments
However, if I remove T type parameter from my pageElementRenderer class, everything works fine. Did I miss something?
Thanks guys, adding x:TypeArguments="local:SomeClass" solved the problem!
Related
While it is easy to implement the xaml definition of a child window/page using x:TypeArguments, such as
// ParentWindowClass.cs
public abstract class ParentWindowClass<T> : Window { ... }
// ChildWindowClass
public class ChildWindowClass : ParentWindowClass<double> { ... }
<ParentWindowClass x:Class="MyNamespace.ChildWindowClass" x:TypeArguments="sys:Double">
How can we define the xaml code for a generic class if we want to allow any type to be used as type parameter? For example:
// GenericDialog.xaml.cs
public partial class GenericDialog<T> : Window
{
T SomePublicProperty { get; }
public GenericDialog()
{
InitializeComponent();
// The name 'InitializeComponent' does not exist in the current context
// (This is because x:Class is incomplete in the xaml file)
}
}
<!-- GenericDialog.xaml -->
<Window x:class="MyNamespace.GenericDialog">
<!-- x:TypeArguments doesn't apply here because it can be used for any type -->
The end goal is the following use cases that all reuse the same xaml:
new GenericDialog<int>().Show()
new GenericDialog<DateTime>().Show()
I have this class:
public class EditorKey
{
public Type TargetType { get; set; }
public DataTemplate Template { get; set; }
}
Now, I want to create an instance of this class in XAML. Since in UWP we don't have the x:Type markup extension, I'm specifying the type directly as a string, with the correct prefix with TargetType="model:Customer"
<Page
x:Class="App8.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:model="using:App8"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<ContentControl>
<model:EditorKey TargetType="model:Customer" />
</ContentControl>
</Page>
Running this, I get a runtime exception:
Failed to create a 'App8.EditorKey' from the text 'model:Customer'.
How can I map the string to the actual Type?
The usual way of doing it in UWP is to simply supply the reference as a string:
<model:EditorKey TargetType="model:Customer" />
If this doesn't work, try specifying the full namespace, rather than defining an xmlns.
Example:
<model:EditorKey TargetType="App8.Customer" />
Note: As of time of writing, there's an issue where the above will crash in Release mode. As a workaround, you can create a Markup extension:
[MarkupExtensionReturnType(ReturnType = typeof(Type))]
public sealed class TypeExtension : MarkupExtension
{
public Type Type { get; set; }
/// <inheritdoc/>
protected override object ProvideValue() => Type;
}
And use it like so:
<model:EditorKey TargetType="{local:Type Type=model:Customer"/>
Here's my wish to create something abstract in WPF. You've got a main window (called main for example, even if it's not correct we don't care) with two buttons. Those two buttons have the same function : they open a new window, the same for both of them, but with different things inside. So I decided to create an abstract class to rule them like that :
public abstract (partial ?) class A : Window
{
public A()
{
InitializeComponent(); // Not sure about that, it's kinda weird to use it here no ?
}
...
}
public partial class B : A
{
public B()
{
InitializeComponent(); // Since it's already in A I shouldn't have to use it here right ?
}
...
}
public partial class C : A
{
public C()
{
InitializeComponent(); // Same thing here...
}
...
}
Debugging gives me something like : "error CS0263: Partial declarations of 'namespace.B' must not specify different base classes".
Removing 'partial' from A class gives : "error CS0260: Missing partial modifier on declaration of type 'namespace.A'; another partial declaration of this type exists".
I know that 'partial' specifies if a class has another part of her somewhere else (like the xaml file beside the cs one), so I guess the abstract class has to be partial too since it contains my controls. Maybe the B class shouldn't be partial ?
I know (memories) it works with Windows Forms, but there's no xaml files so it's easier, and I didn't find any useful tips. I think I understood that this problem occurs when I don't change something in my xaml file, which doesn't works as simply as "class : abstract class". Maybe the subclass thing ?
Please note that I'm a beginner in WPF apps, so I thank you in advance if your answer is as detailed as possible.
Thanks !
You need to define your base class all in code without using XAML :
Code for class A :
public abstract class A : Window { }
Code for class B :
public partial class B : A
{
public B()
{
InitializeComponent();
}
}
xaml for class B :
<yourNamespace:A x:Class="yourNamespace.B"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:yourNamespace="clr-namespace:yourNamespace"...
A C# partial class is one that is defined over two or more source files. The other 'part' of Window is generated by Visual Studio, and includes things like InitializeComponent()
If you want a base Window class, then it has to be defined entirely in code, and have no XAML component. It will then no longer be a partial class.
public abstract class WindowA : Window
{
// define base methods here
}
Then you can derive WindowB from WindowA:
public partial class WindowB : WindowA
{
public WindowB()
{
InitializeComponent();
}
}
But you also need to do it in the other 'part', i.e. in XAML, so in WindowB's XAML file, the root Window tag needs to be changed to:
<wpfApp:WindowA x:Class="WpfApp.WindowB"
wpfApp:WindowA ="clr-namespace:WpfApp"
(you'll need to change the namespace appropriately)
This will generate the other 'part' deriving from WindowA, so there will be no inconsistency.
The InitializeComponent() method should be called in each derived class' constructor, as it's specific to that class, i.e. in this case, the other 'part' is generated from the XAML, and defines an InitializeComponent() with resourceLocator code that specific to WindowB.
I'm new in WPF and I'm doing following code below as:
public partial class MainView : ViewModelControl
{
public MainView()
{
InitializeComponent();
this.DataContext = new ViewModel1();
}
}
As you can see I have my own class ViewModelControl. I have the following code below fro ViewModelControl class as:
public partial class ViewModelControl : Window, IViewModelControl
{
public virtual void bindFirstDataContext()
{
}
public virtual void bindSecondDataContext()
{
}
}
I Modify the following XAML as:
<Window x:Class="WpfApplication1.View.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainView" Height="262" Width="316">
Now the issue is that its show's error "Partial declarations of 'WpfApplication1.View.MainView' must not specify different base classes". It's not working as before as When I implement it with Window class as by default. Where I'm doing wrong?
Base class for partial class declarations should be same. You have changed Window in code behind to ViewModelControl so that needs to be updated in XAML as well.
Change XAML declaration to this:
<local:ViewModelControl
x:Class="WpfApplication1.View.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Namespace"> <-- HERE replace Namepsace with
actual namespace of class
ViewModelControl.
....
</local:ViewModelControl/>
You need to change your XAML:
<src:ViewModelControl x:Class="WpfApplication1.Interface.MainView"
xmlns:src="clr-namespace:WpfApplication1.Interface" ...>
</src:ViewModelControl>
This will work, be carefull in the class attribute, write the fullname of your class ("namespace"."name of the class")
After "xmlns:src="clr-namespace:" you need to write the namespace where ViewModelControl is.
It need to be like that because MainView doesn't inherit directly from Window so you can't put a class name in the class attribute of a Window if it's not a direct child of window.
With this change it should work I tried it, if you still have some problem look again the namespace names it's easy to get confused.
I have this code behind:
CustomUserControl.xaml.cs
namespace MyProject
{
public partial class CustomUserControl<T> : UserControl
{
...
}
}
and this xaml:
CustomUserControl.xaml
<UserControl x:Class="MyProject.CustomUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
<Grid>
</Grid>
It doesn't work since the x:Class="MyProject.CustomUserControl" doesn't match the code-behind's generic class definition. Is there some way to make this work?
You can create generic "code-behind" file without XAML-file:
public class CustomUserControl<T>: UserControl
{ }
and than derive from it providing specific class as a parameter:
public partial class SpecificUserControl : CustomUserControl<Presenter>
{
public SpecificUserControl()
{
InitializeComponent();
}
}
XAML:
<application:CustomUserControl
x:TypeArguments="application:Presenter"
xmlns:application="clr-namespace:YourApplicationNamespace"
...
Unfortunately, it seems that Visual Studio designer doesn't support such generics until Visual Studio 2012 Update 2 (see https://stackoverflow.com/a/15110115/355438)
Haven't found my solution anywhere else so far.
The main difference is, that I have got one .xaml file
for the generic user control class and not one for each actual user control.
GenericUserControl.xaml.cs
using System.Windows.Controls;
namespace TestGenericUserControl
{
public abstract partial class GenericUserControl : UserControl
{
// If you use event handlers in GenericUserControl.xaml, you have to define
// them here as abstract and implement them in the generic class below, e.g.:
// abstract protected void MouseClick(object sender, MouseButtonEventArgs e);
}
public class GenericUserControl<T> : GenericUserControl
{
// generic properties and stuff
public GenericUserControl()
{
InitializeComponent();
}
}
// To use the GenericUserControl<T> in XAML, you could define:
public class GenericUserControlString : GenericUserControl<string> { }
// and use it in XAML, e.g.:
// <GenericUserControlString />
// alternatively you could probably (not sure) define a markup extension to instantiate
// it directly in XAML
}
GenericUserControl.xaml
<UserControl x:Class="TestGenericUserControl.GenericUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d">
<Grid>
<Label Content="hello" />
</Grid>
</UserControl>
Unfortunately XAML does not support generic code behind, thou you can walk around this.
See links below:
http://forums.silverlight.net/forums/p/29051/197576.aspx
Can I specify a generic type in XAML (pre .NET 4 Framework)?
May be generic controls will be supported natively in future versions of Visual Studuo with
XAML 2009.
I ended up with an extra helper class, this is not a UserControl. It does the job well for a simple custom UserControl that needs a Generic Type.
So the UserControl, for instance UCMoveObject, has an InitFunction that returns to me the Helper instance:
// In the UCMoveObjects UserControl Class.
public UCMoveObjectsHelper<T> InitUCMoveObject<T>(List<T> argAllList, List<T> argSelectedList)
{
return new UCMoveObjectsHelper<T>(this, argAllList, argSelectedList);
}
Then in the helper class I place all the code for making the user control work.
public class UCMoveObjectsHelper<T>
{
public UCMoveObjectsHelper(UCMoveObjects argUserControl, List<T> argAllList, List<T> argSelected)
{
_ucMoveObjects = argUserControl;
// Example reaching the usercontrol
_ucMoveObjects.listBox.SelectionChanged += LbAll_SelectionChanged;
// Do whatever I want with T
}
public List<T> ReturnSelected()
{
// Code that return a List<T>;
}
}
In the MainWindow.xaml i place a usercontrol in the window. Then in code I do the following:
_ucMovingObjectsHelper = ucMovingObjects.InitUCMoveObject<TblUsers>(tempListAll, tempListSelected);
And later in my code I can call:
var tempSelectedList = _ucMovingObjectsHelper.ReturnSelected();