27

Java 出身の私は、GUI コンポーネントを作成する際の一般的な方法に慣れています。通常、GUI コンポーネントのすべての共通オブジェクトを含むある種の基本クラスを作成し、それを拡張します。

基本的に、これは C# と XAML で実現したいことです。

質問を明確にするために、私がやっていることの例を次に示します (これは機能していません!)。

独自の XAML を持つ基本クラスがあります。

<UserControl x:Class="BaseClass"
    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"
    mc:Ignorable="d"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    d:DesignHeight="480" d:DesignWidth="480">

    <Grid x:Name="LayoutRoot" Background="{StaticResource PhoneChromeBrush}">
        <Border BorderBrush="Aqua" BorderThickness="10" CornerRadius="10" x:Name="Border" HorizontalAlignment="Left" Height="480" VerticalAlignment="Top" Width="480"/>

    </Grid>
</UserControl>

そして、最初のクラスを拡張するクラスがあります

<base:BaseClass x:Class="DerivedClass"
    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:base="clr-namespace:BaseClass"
    mc:Ignorable="d"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    d:DesignHeight="60" d:DesignWidth="200">

    <Grid x:Name="LayoutRoot" Margin="0" Width="200" Height="60" MaxWidth="200" MaxHeight="60" Background="{StaticResource PhoneAccentBrush}">        
        <TextBlock x:Name="dummyText" HorizontalAlignment="Left" Margin="10,10,0,0" TextWrapping="Wrap" Text="Dummy Plugin" VerticalAlignment="Top" Height="40" Width="180" Foreground="White" TextAlignment="Center"/>
    </Grid>
</base:BaseClass>

DerivedClass2 つの XAML コードから始めて、BaseClassコンテナーに を入れたいと思います。これにより、必要なたびにコードを記述することなく、さまざまな派生クラス間でコンポーネントを共有できます。

たとえば、すべてのコンポーネントに丸みを帯びた境界線を持たせたい場合は、それをベース クラスに配置し、それを書き換えずにすべての派生クラスに配置したいと考えています。

もちろん、各 C# クラスには独自のInitializeComponent()メソッドがあり、これはおそらく、派生コンポーネントが基本クラスのコンテンツを削除して独自のコンテンツを構築することを意味します。

コンストラクターからメソッドを削除するDerivedClassと、派生クラスでも基本コンテンツが得られますが、DerivedClass.

から基本コンストラクターを呼び出してもDerivedClass、派生した前に呼び出されるため、効果はありませんInitializeComponent()

問題は、派生クラスの XAML 設計を壊すことなく、基本クラスから派生クラスに XAML 設計を使用するにはどうすればよいかということです。デザイナー自体で作業しながら、コンテンツを基本クラスに単純に追加する方法はありますか?

(派生クラスの XAML を削除して、コードでやりたいことを実行できることはわかっていますが、デザイナーだけでこれを実行できるかどうかを知りたいのです。デザイナーが利用可能)

編集:

HighCore の返信に続いて、Windows Phone で動作することを行いましたが、正しいことを行っているかどうかはわかりません (はい、動作しますが、単に間違っている可能性があります!)。

これが私がしたことです:

BaseControl.xaml

<UserControl x:Class="TestInheritance.BaseControl"
    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"
    mc:Ignorable="d"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    d:DesignHeight="480" d:DesignWidth="480">


     <Grid x:Name="LayoutRoot" Background="{StaticResource PhoneChromeBrush}">        
        <TextBlock HorizontalAlignment="Center">BASE</TextBlock>        
        <ContentPresenter Name="Presenter" Content="{Binding PresenterContent}"/>
    </Grid>
</UserControl>

BaseControl.xaml.cs

namespace TestInheritance
{
    public partial class BaseControl : UserControl
    {

        public Grid PresenterContent { get; set; }        

        public BaseControl()
        {
            DataContext = this;
            InitializeComponent();            
        }
    }
}

DerivedControl.xaml

<local:BaseControl x:Class="TestInheritance.DerivedControl"
    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:TestInheritance"
    mc:Ignorable="d"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    d:DesignHeight="480" d:DesignWidth="480">

    <local:BaseControl.PresenterContent>
        <Grid>
            <TextBlock VerticalAlignment="Bottom" HorizontalAlignment="Center">DERIVED</TextBlock>
        </Grid>
    </local:BaseControl.PresenterContent>
</local:BaseControl>

他の理由でいくつかの共通のプロパティ/メソッドを持つ必要があるため、DerivedClassは のインスタンスであることに注意してください。BaseClass

私の解決策についてどう思いますか? それは理にかなっていますか?

4

2 に答える 2

74

わかりました、これを部分に分割しましょう:

ジャワ出身

ジャバを忘れてください。これは、90 年代から進化していない、本当に時代遅れの言語です。C# は 100 万倍優れており、WPF は最新の最高の UI フレームワークです。

私が見た限りでは、swing などの Java UI フレームワークは概念的には .Net の winforms に似ており、これも WPF に置き換えられています。

WPF (および XAML ベースの同類) は、スタイルとテンプレートによるカスタマイズの拡張機能とDataBindingのサポートにより、他のフレームワークとは根本的に異なります。

このため、WPF を開始する際にはかなりのマインドシフトが必要です。


私は通常、GUI コンポーネントのすべての共通オブジェクトを含むある種の基本クラスを作成し、それを拡張します。

WPF にはContent Modelがあり、「何でも内部に何でも」入れる機能を導入することで、継承やその他の肥大化した不必要な慣行の必要性を取り除きます。

たとえば、 aButtonは次のように定義できます。

<Button>
    <Button.Content>
        <StackPanel Orientation="Horizontal">
            <Ellipse Fill="Red" Height="10" Width="10" Margin="2"/>
            <TextBlock Text="Click Me"/>
        </StackPanel>
    <Button.Content>
 </Button>

その結果、

赤い点のあるボタン

コンテンツを定義するためだけに Button から継承する必要はありません。

WPF には、XAML タグのコンテンツが何を表すContentPropertyかを定義する属性という、非常に便利な追加の利点があります。から派生し、次のように宣言されます。<Button> </Button>ButtonContentControl

//Declaration of the System.Windows.Control.ContentControl class,
//inside the PresentationFramework.dll assembly
//...  
[ContentProperty("Content")]
public class ContentControl: Control //...
{
   //...
}

これは、次の XAML が上記と機能的に同一であることを意味します。

<Button>
   <StackPanel Orientation="Horizontal">
       <Ellipse Fill="Red" Height="10" Width="10" Margin="2"/>
       <TextBlock Text="Click Me"/>
    </StackPanel>
</Button>
  • 属性がそれを処理する<Button.Content>ため、タグを削除したことに注意してください。ContentProperty

これはすべて、 ControlTemplatesと呼ばれる機能のおかげで可能になりました。これは、コントロールの動作とは無関係に、コントロールの視覚的な外観を定義します。


私がやりたいのは、DerivedClass を BaseClass コンテナーに入れることです。

これを実現するにはいくつかの方法がありますが、そのうちの 1 つはControlTemplates、コンテンツをホストする XAML 内の特定のコンテナーを利用して定義することです。

<UserControl x:Class="BaseClass">
    <UserControl.Template>
        <ControlTemplate TargetType="UserControl">
            <DockPanel>
                <TextBlock DockPanel.Dock="Top" Text="I'm the Container"/>

                <!-- This is where the Hosted Content will be placed -->
                <ContentPresenter ContentSource="Content"/>
            </DockPanel>
        </ControlTemplate>
     </UserControl.Template>
</UserControl>

次に、このテンプレートを次のように再利用できます。

<Window>
   <my:BaseClass>
       <Border Background="Gray" BorderBrush="Blue" BorderThickness="2"
               VerticalAlignment="Center" HorizontalAlignment="Center">
           <TextBlock Text="Im the Hosted Content" Foreground="AliceBlue"/>
       </Border>
   </my:BaseClass>
</Window>

その結果:

アプリケーション ウィンドウ

継承や手続き型コードは必要ありません。


上記の「大幅なマインドシフト」リンクで詳しく説明されている、WPF を開始する際のもう 1 つの非常に重要な側面は、ここで皆さんにお伝えすることです。

WPF で 1 行のコードを記述する前に MVVM について学習する

  • ほとんどの場合、WPF UI 要素にコードを配置しません。これは、ほとんどのことがDataBinding(上記の「DataBinding」リンクで説明されている)、またはReusable Attached BehaviorsまたはAttached Propertiesを実装することによって実現できるためです。データやビジネス ロジックを処理しないVIEW 固有のコードのみをコード ビハインドに配置する必要があります。

  • 次のような他のフレームワークで使用されるボイラープレート:

    txtLastName.Text = person.LastName;
    txtFirstName.Text = person.FirstName;
    btnSubmit.IsEnabled = person.IsActive;
    

    そのようなものは、DataBinding のために、WPF では完全に不要です。


UI にデータを表示する際に高い柔軟性を可能にするもう 1 つの概念は WPFDataTemplatesです。これにより、一部のデータ型が画面に「レンダリング」されるときに使用される特定の UI を定義できます。


上記のすべてのために、WPF はそこにあるほとんどの UI フレームワークとは根本的に異なり、したがって、他のフレームワークで一般的なすべての恐ろしいボイラープレートとハックの必要性を取り除きます。

提供されているすべてのリンクを読み、アプリケーションの構造と一般的な UI を定義する際に、これらすべての概念と実践を念頭に置くことをお勧めします。

さらにサポートが必要な場合はお知らせください。

于 2013-08-31T16:33:51.597 に答える
0

私の知る限り、リソース XAML (ex のスタイル) でない限り、UI をそのまま派生させることはできません。その理由は、UI マネージャーが拡張 XAML コードを配置する場所を推測できないためかもしれません。ベース UI を次のように想像してください。

<UserControl > 
 <Grid>
  <Border/>
 </Grid>
</UserControl>

および派生 UI :

<UserControl> 
 <DockPanel>
  <Button/>
 </DockPanel>
</UserControl>

ミックス後の結果はどうなりますか?

簡単な答えは次のとおりです。

  <UserControl > 
    <Grid>
     <Border/>
    </Grid>
    <DockPanel>
     <Button/>
    </DockPanel>
  </UserControl>

これは不可能です。

コントロールの埋め込み/互換性の問題はどうですか?

于 2013-08-31T10:25:42.577 に答える