2011-08-24 8 views
10

Jestem pewien, że jest to łatwe, ale nowe dla mnie dla WPF przy użyciu C#. Wiem o dziedziczenie z klasy i zrobili tak wiele razy, tak jak w C# WinForms projektów ...Zrozumienie WPF wywodzących klasę WIndow

public class MyClass : DerivedFromClass 
{} 

jednak zakłopotany w WPF i tu jest problem. Chcę zbudować własny zestaw elementów sterujących, który będzie używany jako baza dla nowego projektu edukacyjnego ... wstępnie ustawić własne style, kolory, tła i inne funkcje. Nie ma problemu. Najpierw uruchom okno WPF i utwórz "MyWindow".

Teraz chcę wziąć tę linię bazową "MyWindow" i jej podklasę dla jeszcze jednej klasy MySubClassedWindow. Tak więc tworzę nową klasę okna i domyślnie VS2010 buduje zarówno część projektanta, jak i kodu formularza. Robię widoku kodu na MySubClassedWindow i znaleźć

partial class MySubclassedWindow : Window 
{} 

w C# za pomocą WinForm, chciałbym tylko zmienić (i podaję odniesienie biblioteki klasy, który zawiera „MyWindow” deklarację.

partial class MySubclassedWindow : MyWindow 
{} 

Kiedy zrobić, pojawia się błąd kompilacja

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

Jak wygląda twoja deklamacja XAML (nie xaml.cs)? –

Odpowiedz

26

klasy bazowej powinny być tylko plik klasy (a nie Window).

więc tworzyć WindowBase.cs

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

W MainWindow (na przykład) zmienić plik xaml.cs dziedziczyć z WindowBase zamiast

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

W MainWindow.xaml, zawierać nazw dla WindowBase i zmień Window to base: WindowBase podobny do tego:

<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

Yup ... to by to zrobiło ... To oczywiste (NIE) ... Dodatkowo wyjaśnienie, że moja deklaracja klasy BASE musi być cały kod, a nie przez Xaml, było to, co zrobiłem podczas tworzenia WinForms, więc część była łatwa do przewidzenia. Dzięki – DRapp

+0

Nie zapomnij dodać 'FrameworkElement.StyleProperty.OverrideMetadata (typeof (Window), new FrameworkPropertyMetadata (TryFindResource (typeof (Window))))' 'in you AppStartup event. W przeciwnym razie klasy pochodne nie będą używały domyślnych stylów okien. – adrianm

+0

Ładny link do wpisu na blogu z innego podobnego pytania - [Tworzenie klasy okna podstawowego w WPF] (http://weblogs.asp.net/psheriff/archive/2009/11/02 /creating-a-base-window-class-in-wpf.aspx) – akjoshi

3

Posiadanie klasy okna podstawowego przynosi krytyczną wadę, a mianowicie wiązanie do właściwości w twojej klasie bazowej są o wiele trudniejsze do wykonania (a aktualnie zaakceptowana odpowiedź nie rozwiązuje tego problemu). Jaki jest cel dziedziczenia, jeśli nie możesz odwoływać się do właściwości bazowych? Po długich godzinach zorientowałem się, jak to ustawić, i chciałem podzielić się nadziejami, że inni będą oszczędzać ten ból.

Konieczne może być użycie takich rzeczy jak konwertery wartości, do których można odwoływać się tylko poprzez statyczne powiązanie, co w moim przypadku miało sens w klasie WindowBase. Podaję przykład, ponieważ trudno jest używać tych konwerterów konsekwentnie zarówno w trybie projektowania, jak i uruchamiania.

Nie można ustawić właściwości x: Name tego odziedziczonego okna za pośrednictwem XAML, ale może nie być konieczne, aby użyć poniższej metody. Podaję przykład jak ustawić nazwę, ponieważ dziedziczenie z Window nie pozwoli ci ustawić nazwy w czasie projektowania w podklasie. Nie polecam polegania na nazwie okna w czasie projektowania, ale ustawienie d: DataContext powinno zająć się potrzebami wiążącymi.

Należy pamiętać, że w trybie projektowania, ale nie w trybie uruchamiania, kopia WindowBase (lub klasa określona w d: DataContext) zostanie utworzona w trybie projektowania i wykorzystana jako kontekst powiązania. W bardzo szczególnych przypadkach mogą wystąpić rozbieżności w danych, ale w większości przypadków takie podejście powinno wystarczyć.

WindowBase.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> 

Nic nie jest potrzebny w kodzie SubClassWindow za (nawet konstruktora).