次の方法で共有


コントロールとは

Windows Presentation Foundation (WPF) には、 ButtonLabelTextBoxMenuListBoxなど、ほぼすべての Windows アプリで使用される多くの一般的な UI コンポーネントが付属しています。 これまで、これらのオブジェクトはコントロールと呼ばれます。 "control" という用語は、アプリ内の可視オブジェクトを表す任意のクラスを意味するために緩やかに使用されます。 目に見えるプレゼンスを持つには、クラスが Control クラスから継承する必要はないという点に注意することが重要です。 Control クラスから継承するクラスにはControlTemplateが含まれています。これにより、コントロールのコンシューマーは、新しいサブクラスを作成しなくても、コントロールの外観を根本的に変更できます。 この記事では、コントロール ( Control クラスから継承されるコントロールと継承しないコントロールの両方) を WPF で一般的に使用する方法について説明します。

コントロールのインスタンスを作成する

拡張可能なアプリケーション マークアップ言語 (XAML) またはコードを使用して、アプリにコントロールを追加できます。 たとえば、ユーザーに名前とアドレスを要求する WPF ウィンドウの次の画像を考えてみましょう。

名前とアドレスのラベルが付いた 2 つのテキスト ボックスを含む WPF アプリのスクリーンショット。2 つのボタンが表示されます。1 つのボタンには

このウィンドウには、2 つのラベル、2 つのテキスト ボックス、2 つのボタンの 6 つのコントロールがあります。 次のスニペットに示すように、XAML を使用してこれらのコントロールを作成します。

<Window x:Class="Examples.ExampleApp"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Input Record" Height="Auto" Width="300" SizeToContent="Height">
    <Grid Margin="5">
        <Grid.RowDefinitions>
            <RowDefinition Height="30"/>
            <RowDefinition Height="30"/>
            <RowDefinition Height="30"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>

        <Label>Enter your name:</Label>
        <TextBox Grid.Row="0" Grid.Column="1" Name="FirstName" Margin="2" />

        <Label Grid.Row="1">Enter your address:</Label>
        <TextBox Grid.Row="1" Grid.Column="1" Name="LastName" Margin="2" />

        <Button Grid.Row="2" Grid.Column="0" Name="Reset" Margin="2">Reset</Button>
        <Button Grid.Row="2" Grid.Column="1" Name="Submit" Margin="2">Submit</Button>
    </Grid>
</Window>

すべてのコントロールは、XAML でも同様に作成できます。 同じウィンドウをコードで作成できます。

// Grid container which is the content of the Window
Grid container = new() { Margin = new Thickness(5) };
container.RowDefinitions.Add(new RowDefinition());
container.RowDefinitions.Add(new RowDefinition());
container.RowDefinitions.Add(new RowDefinition());
container.ColumnDefinitions.Add(new ColumnDefinition());
container.ColumnDefinitions.Add(new ColumnDefinition());

// Create the two labels, assign the second label to the second row
Label labelName = new() { Content = "Enter your name:" };
container.Children.Add(labelName);

Label labelAddress = new() { Content = "Enter your address:" };
Grid.SetRow(labelAddress, 1);
container.Children.Add(labelAddress);

// Create the two textboxes, assign both to the second column and
// assign the second textbox to the second row.
TextBox textboxName = new() { Margin = new Thickness(2) };
Grid.SetColumn(textboxName, 1);
container.Children.Add(textboxName);

TextBox textboxAddress = new() { Margin = new Thickness(2) };
Grid.SetRow(textboxAddress, 1);
Grid.SetColumn(textboxAddress, 1);
container.Children.Add(textboxAddress);

// Create the two buttons, assign both to the third row and
// assign the second button to the second column.
Button buttonReset = new() { Margin = new Thickness(2), Content = "Reset" };
Grid.SetRow(buttonReset, 2);
container.Children.Add(buttonReset);

Button buttonSubmit = new() { Margin = new Thickness(2), Content = "Submit" };
Grid.SetColumn(buttonSubmit, 1);
Grid.SetRow(buttonSubmit, 2);
container.Children.Add(buttonSubmit);

// Create the popup window and assign the container (Grid) as its content
Window inputWindow = new()
{
    Title = "Input Record",
    Height = double.NaN,
    Width = 300,
    SizeToContent = SizeToContent.Height,
    Content = container
};

inputWindow.Show();
' Grid container which is the content of the Window
Dim container As New Grid() With {.Margin = New Thickness(5)}
container.RowDefinitions.Add(New RowDefinition())
container.RowDefinitions.Add(New RowDefinition())
container.RowDefinitions.Add(New RowDefinition())
container.ColumnDefinitions.Add(New ColumnDefinition())
container.ColumnDefinitions.Add(New ColumnDefinition())

' Create the two labels, assign the second label to the second row
Dim labelName As New Label() With {.Content = "Enter your name:"}
container.Children.Add(labelName)

Dim labelAddress As New Label() With {.Content = "Enter your address:"}
Grid.SetRow(labelAddress, 1)
container.Children.Add(labelAddress)

' Create the two textboxes, assign both to the second column and
' assign the second textbox to the second row.
Dim textboxName As New TextBox() With {.Margin = New Thickness(2)}
Grid.SetColumn(textboxName, 1)
container.Children.Add(textboxName)

Dim textboxAddress As New TextBox() With {.Margin = New Thickness(2)}
Grid.SetRow(textboxAddress, 1)
Grid.SetColumn(textboxAddress, 1)
container.Children.Add(textboxAddress)

' Create the two buttons, assign both to the third row and
' assign the second button to the second column.
Dim buttonReset As New Button() With {.Margin = New Thickness(2), .Content = "Reset"}
Grid.SetRow(buttonReset, 2)
container.Children.Add(buttonReset)

Dim buttonSubmit As New Button() With {.Margin = New Thickness(2), .Content = "Submit"}
Grid.SetColumn(buttonSubmit, 1)
Grid.SetRow(buttonSubmit, 2)
container.Children.Add(buttonSubmit)

' Create the window and assign the container (Grid) as its content
Dim inputWindow As New Window() With
{
    .Title = "Input Record",
    .Height = Double.NaN,
    .Width = 300,
    .SizeToContent = SizeToContent.Height,
    .Content = container
}

inputWindow.Show()

イベントの購読

XAML またはコードを使用してコントロールのイベントをサブスクライブできますが、処理できるのはコード内のイベントのみです。

XAML では、イベントは要素の属性として設定されます。 イベントに <Element.Event>handler<Element.Event> 表記を使用することはできません。 次のスニペットは、ClickButtonイベントを購読する方法を示しています。

<Button Click="Submit_Click" Grid.Row="2" Grid.Column="1" Name="Submit" Margin="2">Submit</Button>

コードで同じ操作を行う方法を次に示します。

Button buttonSubmit = new() { Margin = new Thickness(2), Content = "Submit" };
buttonSubmit.Click += Submit_Click;
Dim buttonSubmit As New Button() With {.Margin = New Thickness(2), .Content = "Submit"}
AddHandler buttonSubmit.Click, AddressOf Submit_Click

次のスニペットは、ClickButton イベントを処理します。

private void Submit_Click(object sender, RoutedEventArgs e)
{
    MessageBox.Show("Someone clicked the submit button.");
}
Private Sub Submit_Click(sender As Object, e As Windows.RoutedEventArgs)
    MessageBox.Show("Someone clicked the submit button.")
End Sub

コントロールの外観を変更する

アプリの外観に合わせてコントロールの外観を変更するのが一般的です。 コントロールの外観を変更するには、実行する内容に応じて、次のいずれかを実行します。

  • コントロールのプロパティの値を変更します。
  • コントロールの Style を作成します。
  • コントロールの新しい ControlTemplate を作成します。

コントロールのプロパティを変更する

多くのコントロールには、ボタンの背景など、コントロールの表示方法を変更できるプロパティがあります。 値のプロパティは、XAML とコードの両方で設定できます。 次の例では、XAML のBackgroundFontSizeFontWeight、およびButtonプロパティを設定します。

<Button Grid.Row="2" Grid.Column="1" Name="Submit" Margin="2" Content="Submit">
    <Button.FontSize>18</Button.FontSize>
    <Button.FontWeight>Bold</Button.FontWeight>
    <Button.Background>
        <LinearGradientBrush>
            <GradientStop Color="#0073E6" Offset="0.0" />
            <GradientStop Color="#81D4FA" Offset="0.9" />
        </LinearGradientBrush>
    </Button.Background>
</Button>

コードで同じ操作を行う方法を次に示します。

Button buttonSubmit = new() { Margin = new Thickness(2), Content = "Submit" };
buttonSubmit.FontSize = 18f;
buttonSubmit.FontWeight = FontWeights.Bold;
buttonSubmit.Background =
    new LinearGradientBrush(
        (Color)ColorConverter.ConvertFromString("#0073E6"),
        (Color)ColorConverter.ConvertFromString("#81D4FA"),
        new Point(0d, 0d),
        new Point(0.9d, 0d));
Dim buttonSubmit As New Button() With {.Margin = New Thickness(2), .Content = "Submit"}
buttonSubmit.FontSize = 18.0F
buttonSubmit.FontWeight = FontWeights.Bold
buttonSubmit.Background =
        New LinearGradientBrush(
            ColorConverter.ConvertFromString("#0073E6"),
            ColorConverter.ConvertFromString("#81D4FA"),
            New Point(0D, 0D),
            New Point(0.9D, 0D))

この例のウィンドウは次の図のようになります。

名前とアドレスのラベルが付いた 2 つのテキスト ボックスを含む WPF アプリのスクリーンショット。2 つのボタンが表示されます。1 つのボタンには

コントロールのスタイルを作成する

WPF では、各コントロールにプロパティを設定するのではなく、 Styleを作成してコントロールの外観を指定する広範な機能が提供されます。 Style定義は、通常、コントロールやウィンドウのResourceDictionary プロパティなど、Resourcesの XAML で定義されます。 リソースは、宣言されているスコープに適用されます。 詳細については、「 XAML リソースの概要」を参照してください。

次の例では、スタイルを定義する同じStyleに含まれるすべてのButtonGridを適用します。

<Grid.Resources>
    <Style TargetType="{x:Type Button}">
        <Style.Setters>
            <Setter Property="FontSize" Value="18" />
            <Setter Property="FontWeight" Value="Bold" />
            <Setter Property="Background">
                <Setter.Value>
                    <LinearGradientBrush>
                        <GradientStop Color="#0073E6" Offset="0.0" />
                        <GradientStop Color="#81D4FA" Offset="0.9" />
                    </LinearGradientBrush>
                </Setter.Value>
            </Setter>
        </Style.Setters>
    </Style>
</Grid.Resources>

コードで同じ操作を行う方法を次に示します。

Grid container = new() { Margin = new Thickness(5) };
container.RowDefinitions.Add(new RowDefinition());
container.RowDefinitions.Add(new RowDefinition());
container.RowDefinitions.Add(new RowDefinition());
container.ColumnDefinitions.Add(new ColumnDefinition());
container.ColumnDefinitions.Add(new ColumnDefinition());

Style buttonStyle = new(typeof(Button));
buttonStyle.Setters.Add(new Setter(Button.FontSizeProperty, 18d));
buttonStyle.Setters.Add(new Setter(Button.FontWeightProperty, FontWeights.Bold));
buttonStyle.Setters.Add(new Setter(Button.BackgroundProperty,
    new LinearGradientBrush(
        (Color)ColorConverter.ConvertFromString("#0073E6"),
        (Color)ColorConverter.ConvertFromString("#81D4FA"),
        new Point(0d, 0d),
        new Point(0.9d, 0d))));

container.Resources.Add(typeof(Button), buttonStyle);
Dim container As New Grid() With {.Margin = New Thickness(5)}
container.RowDefinitions.Add(New RowDefinition())
container.RowDefinitions.Add(New RowDefinition())
container.RowDefinitions.Add(New RowDefinition())
container.ColumnDefinitions.Add(New ColumnDefinition())
container.ColumnDefinitions.Add(New ColumnDefinition())

Dim buttonStyle As New Style(GetType(Button))
buttonStyle.Setters.Add(New Setter(Button.FontSizeProperty, 18.0R))
buttonStyle.Setters.Add(New Setter(Button.FontWeightProperty, FontWeights.Bold))
buttonStyle.Setters.Add(New Setter(Button.BackgroundProperty,
        New LinearGradientBrush(
            ColorConverter.ConvertFromString("#0073E6"),
            ColorConverter.ConvertFromString("#81D4FA"),
            New Point(0D, 0D),
            New Point(0.9D, 0D))))

container.Resources.Add(GetType(Button), buttonStyle)

次の図は、2 つのボタンの外観を変更するウィンドウのグリッドに適用されるスタイルを示しています。

名前とアドレスのラベルが付いた 2 つのテキスト ボックスを含む WPF アプリのスクリーンショット。2 つのボタンが表示されます。1 つのボタンには

特定の種類のすべてのコントロールにスタイルを適用する代わりに、リソース ディクショナリ内のスタイルにキーを追加し、コントロールの Style プロパティでそのキーを参照することで、特定のコントロールに割り当てることもできます。 スタイルの詳細については、「 スタイル設定とテンプレート」を参照してください。

ControlTemplate を作成する

Styleを使用すると、一度に複数のコントロールにプロパティを設定できますが、Styleで実行できる範囲を超えてコントロールの外観をカスタマイズしたい場合があります。 Control クラスから継承するクラスには、コントロールの構造と外観を定義するControlTemplateがあります。

ほぼすべてのアプリで使用される一般的なコントロールである Button コントロールについて考えてみましょう。 ボタンの主な動作は、ユーザーがボタンを選択したときにアプリが何らかのアクションを実行できるようにすることです。 既定では、WPF のボタンは上げられた四角形として表示されます。 アプリを開発する際に、ボタンの動作、つまりユーザーがボタンを操作してClickイベントを発生させる動作を利用することができますが、ボタンのプロパティを変更するだけではできないようなボタンの外観の変更も可能です。 この場合は、新しい ControlTemplateを作成できます。

以下の例では、ControlTemplateButtonに作成します。 ControlTemplateは、角が丸くグラデーションの背景を持つ境界線を表示するButtonのビジュアルを作成します。

<Button Grid.Row="2" Grid.Column="1" Name="Submit" Margin="2" Content="Submit">
    <Button.Template>
        <ControlTemplate TargetType="Button">
            <Border Name="Border" CornerRadius="10" BorderThickness="1" BorderBrush="Black">
                <Border.Background>
                    <LinearGradientBrush StartPoint="0,0.5" 
                         EndPoint="1,0.5">
                        <GradientStop Color="{Binding Background.Color, RelativeSource={RelativeSource TemplatedParent}}" Offset="0.0" />
                        <GradientStop Color="PeachPuff" Offset="0.9" />
                    </LinearGradientBrush>
                </Border.Background>
                <ContentPresenter Margin="2" HorizontalAlignment="Center" VerticalAlignment="Center" RecognizesAccessKey="True"/>
            </Border>
            <ControlTemplate.Triggers>
                <!--Change the appearance of the button when the user clicks it.-->
                <Trigger Property="IsPressed" Value="true">
                    <Setter TargetName="Border" Property="Background">
                        <Setter.Value>
                            <LinearGradientBrush StartPoint="0,0.5" EndPoint="1,0.5">
                                <GradientStop Color="{Binding Background.Color, RelativeSource={RelativeSource TemplatedParent}}" Offset="0.0" />
                                <GradientStop Color="LightBlue" Offset="0.9" />
                            </LinearGradientBrush>
                        </Setter.Value>
                    </Setter>
                </Trigger>
            </ControlTemplate.Triggers>
        </ControlTemplate>
    </Button.Template>
</Button>

BackgroundButton プロパティは、この例が正常に動作するためにSolidColorBrushに設定されている必要があります。

コードで同じ操作を行う方法を次に示します。 次のコードでは、XAML 文字列を作成し、それを解析して適用できるテンプレートを生成します。これは、実行時にテンプレートを生成するためのサポートされている方法です。

Button buttonSubmit = new() { Margin = new Thickness(2), Content = "Submit" };

// Create the XAML used to define the button template
const string xaml = """
    <ControlTemplate TargetType="Button">
        <Border Name="Border" CornerRadius="10" BorderThickness="1" BorderBrush="Black">
            <Border.Background>
                <LinearGradientBrush StartPoint="0,0.5" 
                     EndPoint="1,0.5">
                    <GradientStop Color="{Binding Background.Color, RelativeSource={RelativeSource TemplatedParent}}" Offset="0.0" />
                    <GradientStop Color="PeachPuff" Offset="0.9" />
                </LinearGradientBrush>
            </Border.Background>
            <ContentPresenter Margin="2" HorizontalAlignment="Center" VerticalAlignment="Center" RecognizesAccessKey="True"/>
        </Border>
        <ControlTemplate.Triggers>
            <!--Change the appearance of the button when the user clicks it.-->
            <Trigger Property="IsPressed" Value="true">
                <Setter TargetName="Border" Property="Background">
                    <Setter.Value>
                        <LinearGradientBrush StartPoint="0,0.5" EndPoint="1,0.5">
                            <GradientStop Color="{Binding Background.Color, RelativeSource={RelativeSource TemplatedParent}}" Offset="0.0" />
                            <GradientStop Color="LightBlue" Offset="0.9" />
                        </LinearGradientBrush>
                    </Setter.Value>
                </Setter>
            </Trigger>
        </ControlTemplate.Triggers>
    </ControlTemplate>
    """;

// Load the XAML into a stream that can be parsed
using MemoryStream stream = new(System.Text.Encoding.UTF8.GetBytes(xaml));

// Create a parser context and add the default namespace and 
// the x namespace, which is common to WPF XAML
System.Windows.Markup.ParserContext context = new();
context.XmlnsDictionary.Add("", "http://schemas.microsoft.com/winfx/2006/xaml/presentation");
context.XmlnsDictionary.Add("x", "http://schemas.microsoft.com/winfx/2006/xaml");

// Parse the XAML and assign it to the button's template
buttonSubmit.Template = (ControlTemplate)System.Windows.Markup.XamlReader.Load(stream, context);

// Set the other properties of the button
Grid.SetColumn(buttonSubmit, 1);
Grid.SetRow(buttonSubmit, 2);

// Assign the button to the grid container
container.Children.Add(buttonSubmit);
Dim buttonSubmit As New Button() With {.Margin = New Thickness(2), .Content = "Submit"}

' Create the XAML used to define the button template
Const xaml As String = "
    <ControlTemplate TargetType=""Button"">
        <Border Name=""Border"" CornerRadius=""10"" BorderThickness=""1"" BorderBrush=""Black"">
            <Border.Background>
                <LinearGradientBrush StartPoint=""0,0.5"" 
                     EndPoint=""1,0.5"">
                    <GradientStop Color=""{Binding Background.Color, RelativeSource={RelativeSource TemplatedParent}}"" Offset=""0.0"" />
                    <GradientStop Color=""PeachPuff"" Offset=""0.9"" />
                </LinearGradientBrush>
            </Border.Background>
            <ContentPresenter Margin=""2"" HorizontalAlignment=""Center"" VerticalAlignment=""Center"" RecognizesAccessKey=""True""/>
        </Border>
        <ControlTemplate.Triggers>
            <!--Change the appearance of the button when the user clicks it.-->
            <Trigger Property=""IsPressed"" Value=""true"">
                <Setter TargetName=""Border"" Property=""Background"">
                    <Setter.Value>
                        <LinearGradientBrush StartPoint=""0,0.5"" EndPoint=""1,0.5"">
                            <GradientStop Color=""{Binding Background.Color, RelativeSource={RelativeSource TemplatedParent}}"" Offset=""0.0"" />
                            <GradientStop Color=""LightBlue"" Offset=""0.9"" />
                        </LinearGradientBrush>
                    </Setter.Value>
                </Setter>
            </Trigger>
        </ControlTemplate.Triggers>
    </ControlTemplate>"

' Load the XAML into a stream that can be parsed
Using stream As New MemoryStream(System.Text.Encoding.UTF8.GetBytes(xaml))

    ' Create a parser context and add the default namespace and 
    ' the x namespace, which is common to WPF XAML
    Dim context = New System.Windows.Markup.ParserContext()
    context.XmlnsDictionary.Add("", "http://schemas.microsoft.com/winfx/2006/xaml/presentation")
    context.XmlnsDictionary.Add("x", "http://schemas.microsoft.com/winfx/2006/xaml")

    ' Parse the XAML and assign it to the button's template
    buttonSubmit.Template = System.Windows.Markup.XamlReader.Load(stream, context)
End Using

' Set the other properties of the button
Grid.SetColumn(buttonSubmit, 1)
Grid.SetRow(buttonSubmit, 2)

' Assign the button to the grid container
container.Children.Add(buttonSubmit)

次の図は、適用されたテンプレートの外観を示しています。

名前とアドレスのラベルが付いた 2 つのテキスト ボックスを含む WPF アプリのスクリーンショット。2 つのボタンが表示されます。1 つのボタンには

前の例では、 ControlTemplate は 1 つのボタンに適用されます。 ただし、 ControlTemplateStyle に割り当て、コントロール セクションの スタイルの作成 で説明した方法のように、すべてのボタンに適用できます。

テンプレートで提供される固有の機能を利用する方法の詳細については、「 スタイルとテンプレート」を参照してください。

コントロール内の豊富なコンテンツ

Control クラスから継承するほとんどのクラスには、リッチ コンテンツを含む容量があります。 たとえば、 Label には、文字列、 ImagePanelなどの任意のオブジェクトを含めることができます。 次のクラスは、リッチ コンテンツのサポートを提供し、WPF のほとんどのコントロールの基底クラスとして機能します。