2011-08-24 14 views
10

Estoy seguro de que esto es fácil, pero nuevo para WPF con C#. Sé sobre la herencia de clases y he hecho tantas veces, como en C# WinForms proyectos ...Comprender WPF derivando WIndow clase

public class MyClass : DerivedFromClass 
{} 

Sin embargo, perplejo en WPF y aquí está el problema. Quiero construir mi propio conjunto de controles para usar como referencia para un nuevo proyecto de aprendizaje ... preconfigurar mis propios estilos, colores, fondos y otras funcionalidades. No hay problema. Comience primero con una ventana de WPF y cree "MyWindow".

Ahora, quiero tomar esta línea de base "MyWindow" y la subclase ESO para otra clase de MySubClassedWindow. Por lo tanto, creo una nueva clase de ventana y, de forma predeterminada, VS2010 crea las porciones de diseñador y de código del formulario. Hago código de la vista en el MySubClassedWindow y encuentro

partial class MySubclassedWindow : Window 
{} 

En C# usando Windows Forms, me acaba de cambiar a (y he incluido la referencia de la biblioteca de clases que incluye la declaración "MyWindow".

partial class MySubclassedWindow : MyWindow 
{} 

Cuando lo hago, me sale un error de compilación de

Partial declarations of 'MyNameSpace.MySubclassedWindow' must not specify different base classes 
+0

¿Qué aspecto tiene su declinación XAML (no xaml.cs)? –

Respuesta

26

su clase base debe ser sólo un archivo de clase (no es un Window).

Por lo tanto crear WindowBase.cs

public class WindowBase : Window 
{ 
    // ... 
} 

En MainWindow (por ejemplo) cambie el archivo xaml.cs heredar de WindowBase lugar

public partial class MainWindow : WindowBase 
{ 
    public MainWindow() 
    { 
     InitializeComponent(); 
    } 
    // ... 
} 

En MainWindow.xaml, incluya el espacio de nombres para WindowBase y cambiar la ventana a la base: WindowBase como este

<base:WindowBase x:Class="SubclassWindow.MainWindow" 
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
        xmlns:base="clr-namespace:NamespaceForWindowBase" 
        Title="MainWindow" Height="350" Width="525"> 
    <!--...--> 
</base:WindowBase> 
+0

Sí ... eso es obvio ... Qué obvio (NO) ... Además, la aclaración de que mi declaración de clase BASE debe ser todo código y no a través de xaml fue lo que hice al hacer el desarrollo de WinForms, entonces esa parte fue fácil de anticipar. Gracias – DRapp

+0

No olvides agregar 'FrameworkElement.StyleProperty.OverrideMetadata (typeof (Window), new FrameworkPropertyMetadata (TryFindResource (typeof (Window))));' en tu evento AppStartup. De lo contrario, las clases derivadas no usarán ningún estilo de ventana predeterminado – adrianm

+0

Enlace de publicación de blog agradable de otra pregunta similar - [Creación de una clase de ventana base en WPF] (http://weblogs.asp.net/psheriff/archive/2009/11/02 /creating-a-base-window-class-in-wpf.aspx) – akjoshi

3

Tener una clase de ventana de base trae un inconveniente crítico, a saber que la unión a las propiedades en su clase base son mucho más difíciles de hacer (y la respuesta actualmente aceptada no resuelve este problema). ¿Cuál es el punto de heredar si no puede hacer referencia a las propiedades base? He descubierto cómo configurar esto después de algunas largas horas, y quería compartir la esperanza de que los demás se librarán de este dolor.

Es posible que tenga que usar cosas como los convertidores de valor, que solo se pueden referenciar mediante enlace estático, que en mi caso tenía sentido tener en la clase WindowBase. He incluido un ejemplo porque me resultó difícil utilizar estos conversores de forma consistente tanto en diseño como en modo de ejecución.

No puede establecer la propiedad x: Name de esta Ventana heredada a través de XAML, pero puede que no sea necesario hacerlo si utiliza el siguiente enfoque. He incluido un ejemplo de cómo configurar el nombre, porque heredar de la ventana no le permitirá establecer el nombre en tiempo de diseño en la subclase. No recomiendo confiar en el nombre de la ventana en el momento del diseño, pero el ajuste d: DataContext debe encargarse de cualquier necesidad de enlace para usted.

Tenga en cuenta que en modo de diseño, pero no modo de ejecución, se creará una instancia de una copia de WindowBase (o la clase especificada en d: DataContext) en modo de diseño y se utilizará como contexto de enlace. Entonces, en casos muy específicos, puede ver discrepancias de datos, pero en la gran mayoría de los casos de uso, este enfoque debería ser suficiente.

WindowBase.Se necesita cs

`` ``

public class WindowBase : Window 
{ 
    //User-Defined UI Configuration class containing System.Drawing.Color 
    //and Brush properties (platform-agnostic styling in your Project.Core.dll assembly) 
    public UIStyle UIStyle => Core.UIStyle.Current; 

    //IValueConverter that converts System.Drawing.Color properties 
    //into WPF-equivalent Colors and Brushes 
    //You can skip this if you do not need or did not implement your own ValueConverter 
    public static IValueConverter UniversalValueConverter { get; } = new UniversalValueConverter(); 

    public WindowBase() 
    { 
     //Add window name to scope so that runtime properties can be referenced from XAML 
     //(Name setting must be done here and not in xaml because this is a base class) 
     //You probably won't need to, but working example is here in case you do. 
     var ns = new NameScope(); 
     NameScope.SetNameScope(this, ns); 
     ns["window"] = this; 

     //Call Initialize Component via Reflection, so you do not need 
     //to call InitializeComponent() every time in your base class 
     this.GetType() 
      .GetMethod("InitializeComponent", 
       System.Reflection.BindingFlags.Public | 
       System.Reflection.BindingFlags.NonPublic | 
       System.Reflection.BindingFlags.Instance) 
      .Invoke(this, null); 

     //Set runtime DataContext - Designer mode will not run this code 
     this.DataContext = this; 
    } 

    //Stub method here so that the above code can find it via reflection 
    void InitializeComponent() { } 
} 

SubClassWindow.xaml

<local:WindowBase 
     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:YourProjectNamespace" 
     x:Class="YourProjectNamespace.SubClassWindow" 
     mc:Ignorable="d" 
     d:DataContext="{d:DesignInstance Type= {x:Type local:WindowBase}, IsDesignTimeCreatable=True}" 
     Title="SubClassWindow" Height="100" Width="300"> 
    <!--Design-time DataContext is set in d:DataContext. That option does not affect runtime data binding 
     Replace local:WindowBase with local:SubClassWindow if you need to access properties in SubClassWindow--> 
    <Grid Background="{Binding UIStyle.BackgroundColor, Converter={x:Static local:WindowBase.UniversalValueConverter}}"></Grid> 
</local:WindowBase> 

Nada en el código SubClassWindow atrás (ni siquiera un constructor).