web-dev-qa-db-ja.com

XAML UserControlの継承

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>
_

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

たとえば、すべてのコンポーネントの境界線を丸くしたい場合は、それを低音クラスに入れてから、派生クラスすべてに書き直さずに入れたいです。

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

DerivedClassコンストラクターからメソッドを削除すると、派生クラスでも基本コンテンツが得られますが、もちろん、DerivedClassのXAMLデザインウィンドウで作成したものはすべて失われます。

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

質問は次のとおりです。派生クラスのXAMLデザインを壊さずに、基本クラスから派生クラスにXAMLデザインを使用するにはどうすればよいですか。デザイナー自体と連携しながら、基本クラスにコンテンツを単純に追加する方法はありますか?

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

編集:

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>
_

DerivedClassBaseClassのインスタンスであることに注意してください。他の理由でいくつかの一般的なプロパティ/メソッドが必要になります。

私のソリューションについてどう思いますか?それは理にかなっていますか?

24
StepTNT

さて、これをいくつかの部分に分けましょう。

Javaから来る

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

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

WPF(およびそのXAMLベースの兄弟)は、 Styles and Templates によるカスタマイズの拡張機能と DataBinding のサポートにより、他のフレームワークと根本的に異なります。

このため、WPFで開始する場合は、 Significant Mindshift が必要です。


私は通常、GUIコンポーネントのすべての共通オブジェクトを含む何らかの基本クラスを実行し、それを拡張します。

WPFには、 コンテンツモデル があります。これは、「何でも何でも」に入れる機能を導入することにより、継承や他の肥大化した不要なプラクティスの必要性を取り除きます。

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

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

結果として

A button with a red dot

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

WPFには、XAMLタグ<Button> </Button>が表す内容を定義する ContentProperty 属性という便利な追加の利点があります。 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>
  • ContentProperty属性がそれを処理するため、<Button.Content>タグを削除したことに注意してください。

これはすべて、 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>

その結果:

An application window

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


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

WPFでコードを1行書く前にMVVMを学ぶ

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

  • 次のような他のフレームワークで使用される定型文

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

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


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


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

一般的にアプリケーションの構造とUIを定義するときは、提供されているすべてのリンクを読んで、これらの概念とプラクティスをすべて念頭に置いておくことをお勧めします。

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

63

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

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

および派生UI:

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

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

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

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

それは不可能です。

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

0
HichemSeeSharp