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.
Related
I'm using Prism 6.1.0 for the first time.
I created a new WPF project just to try the taste of the newest prism.
My shell looks this way:
<Window x:Class="Prism.Shell"
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:Prism"
mc:Ignorable="d"
Title="Shell"
Height="300"
Width="300">
<Grid Background="HotPink"></Grid>
using System.ComponentModel.Composition;
using System.Windows;
namespace Prism
{
/// <summary>
/// Interaction logic for Shell.xaml
/// </summary>
[Export]
public partial class Shell : Window
{
public Shell()
{
InitializeComponent();
}
}
}
Bootstrapper:
using Prism.Mef;
using System.ComponentModel.Composition.Hosting;
using System.Windows;
namespace Prism
{
public class MyBootstrapper : MefBootstrapper
{
protected override DependencyObject CreateShell()
{
return Container.GetExportedValue<Shell>();
}
protected override void InitializeShell()
{
base.InitializeShell();
Application.Current.MainWindow = (Window)Shell;
Application.Current.MainWindow.Show();
}
protected override void ConfigureAggregateCatalog()
{
base.ConfigureAggregateCatalog();
AggregateCatalog.Catalogs
.Add(new AssemblyCatalog(typeof(MyBootstrapper).Assembly));
}
}
}
and App c# code is:
using System.Windows;
namespace Prism
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
(new MyBootstrapper()).Run();
}
}
}
After this line:
I have an exception:
Exception thrown: 'System.InvalidOperationException' in PresentationCore.dll
Additional information: The calling thread must be STA, because many UI components require this.
Does someone know how to fix that?
The exception says that your window constructor is invoked by another thread. In WPF world every control: window, button, etc. must be created by a single special thread called STAThread. Furthermore, only STAThread can access UI controls (if another thread does this, same InvalidOperationExcepiotion would be thrown). So it looks like you invoke a new thread somewhere. I can't see a mistake in your code, I also don't know whether your container starts any thread. But make sure you don't create any suspicious thread in your code.
Also look for StartupURI in App.xaml. It shouldn't be set if you use DI container. But it is not this I think.
I created an example app, I copied your code and it works. So your error is something strange.
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.
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.
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();