WPF UserControl with generic code-behind - c#

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();

Related

How to implement a xaml page for a generic-typed window/page for any type?

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()

Add Generics to XAML cs class

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!

C# - WPF - Changing the inheritance of my window

Hello StackOverflow community,
I got a question regarding the inheritance of the WPF windows.
If you create a new window it inherits from System.Windows.Window - so let's just say: I want that every window that I use have following code in his constructor:
MinHeight = Height;
MinWidth = Width;
So my first attempt was that I create a new class called BaseWindow and put the code block into the constructor of BaseWindow (this method I also often used for forms application and it worked there).
namespace MyProject.Classes
{
public class BaseWindow : System.Windows.Window
{
public BaseWindow()
{
MinHeight = Height;
MinWidth = Width;
}
}
}
My BaseWindow class is now existing and now I let my window wndLogin inherit from the BaseWindow - so something like that:
using MyProject.Classes;
namespace MyProject
{
public partial class wndLogin : BaseWindow
{
public wndLogin()
{
InitializeComponent();
InitializeWindow();
}
private void InitializeWindow()
{
// Initializing window specific stuff
}
}
}
I noticed that this class is partial, so I also changed the inheritance on the other part of the class in the file wndLogin.g.i.cs - it says there, that this file is an auto generated file and should not be edited.
namespace MyProject {
/// <summary>
/// wndLogin
/// </summary>
public partial class wndLogin : System.Windows.Window, System.Windows.Markup.IComponentConnector {
// auto generated stuff here
}
}
When I edit it anyway it replaces my custom inheritance with the standard inheritance → System.Windows.Window after debugging the project.
Is there a way to disable this automatic generated code or to tell Visual Studio that it should use an other class for inheritance?
Thanks in advance! :-)
Don't hand-edit wndLogin.g.i.cs, it is generated automatically (as you found out).
In order to do this, you need to also change the XAML file to use your window:
<local:BaseWindow x:Class="MyNamespace.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MyNamespace"
mc:Ignorable="d"
Title="Window1">
<Grid>
</Grid>
</local:BaseWindow>
Notice the <local:BaseWindow ... instead of Window. If your BaseWindow class is in an entirely different namespace, you'll need to specify that in the xaml:
<baseWindowNs:BaseWindow x:Class="MyNamespace.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MyNamespace"
xmlns:baseWindowNs="clr-namespace:MyProject.Classes"
mc:Ignorable="d"
Title="Window1">
<Grid>
</Grid>
</baseWindowNs:BaseWindow>
The key line being the namespace qualifier: xmlns:baseWindowNs="clr-namespace:MyProject.Classes", and then use your namespace name in place of local from the first sample.
The only other change needed is the WindowName.xaml.cs file to inherit from the new base class:
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : BaseWindow
{
public Window1()
{
InitializeComponent();
}
}
Alternatively (and more in-line with WPF MVVM conventions) you could create a base view model that has those properties and bind your window class Height and Width to those viewmodel properties. Personally that is how I would approach this rather than create a new base class for windows.

DataContext in not working when I implement MainWindow with some other class

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.

Use elements from existing Class in a New Window

in my C# WPF Project (working with VS2012) my goal is to use existing Data from a class in a new Window I created...
Therefore I added a new Window (WPF) to my Project and called it DijkstraWindow. In my MainWindow there is a Menu and when you click the suitable item the DijkstraWindow is opened. In my MainWindow.xaml.cs this is the Code to do this:
private void Dijkstra_Click(object sender, RoutedEventArgs e)
{
var DWindow = new DijkstraWindow();
DWindow.Show();
}
Now I need to access data (which is created while the application is running) and this is stored in a list which is stored in a class. But I have no idea how to do this.
I tried the following:
1.
Creating a new object in DijkstraWindow:
var mwvm = new MainWindowViewModel();
The data is accessible (in my new DijkstraWindow) but it just takes the data which is initialized when starting the application. So this is the wrong way. Because there a some list which is filled while the application is running. I want to use this data in my new Window.
2.
In my DijkstraWindow.xaml.cs I tried to inherit from the class where my data is, but then the compiler is complaining
"Partial declarations must not specify different base classes"
So I read you have also to changed your xaml file, so changed it to:
<local:MainWindowViewModel x:Class="Graphomat.DijkstraWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Graphomat"
Title="DijkstraWindow" Height="300" Width="300">
<Grid/>
</local:MainWindowViewModel>
This is also not working, then my DijkstraWindow has no information about the show method?
Could someone please help me out with this?
Thank you!
edit
Here ist the Class Declaration:
*/using somestuff */
namespace Graphomat
{
/// <summary>
/// Interaction logic for DijkstraWindow.xaml
/// </summary>
public partial class DijkstraWindow : MainWindowViewModel
{
public DijkstraWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
}
}
}
Tried to inherit from class "MainWindowViewModel", but this doesn't work because the xaml file..
The compiler is complaining because your view's type Graphomat.DijkstraWindow doesn't declare the same base type between the xaml and the .cs file. Your cs file likely says that it inherits from the Window type.
One way to transfer data between ViewModels is dependency injection. Consider the following:
public class FooView : Window
{
//require data from the parentview to the child view through dependency injection.
//very simplistic, might meet your needs. If you need a full view lifecycle, see MVVM frameworks like
//cliburn.micro
public FooView(INavigationData navigationData)
{
//do something with your data.
}
}
It is very common to use a base class for all the view models in you project. Consider that you are binding all your views to individual view models, it only makes sense that you would create a base implementation of INotifyPropertyChanged on a base class:
public class MainViewModel : BaseViewModel
{
}
public abstract class BaseViewModel : INotifyPropertyChanged
{
public object Model { get; set; }
#region PropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if(handler != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
#region Commands
public ICommand OpenFooWindowClicked
{
get
{
//implement your ICommand here... beyond the scope of the question.
}
}
#endregion
}
As far as the class problem is concerned, if you are following the typical MVVM naming convention, then it looks like you're trying to define your ViewModel in xaml. While that's not unheard of, you likely want to define your View in xaml.
Please do check out the SO question: MVVM: Tutorial from start to finish? The tutorials linked in that thread should get your head wrapped around the concepts vital to successful execution of the MVVM pattern.

Categories

Resources