WPFのBehaviorを使おうぜ(^~^)?

へろーー(^~^) ぐぎゃー(^~^) 公開下書き

以前の話し

📖 WPFって何だぜ(^~^)?

気の早い人向け

📖 wpf-behavior-practice - Git Hub にソースコードを置いたぜ
📖 wpf-action-practice - Git Hub にソースコードを置いたぜ
📖 wpf-command-practice - Git Hub にソースコードを置いたぜ

ビヘイビア(Behavior)

📖 ビヘイビア(Behavior)の作り方

ramen-tabero-futsu2.png
「 👆 2010年ぐらいの記事だが 古いソースも保守の対称だから ちょうどいいだろう。
ビヘイビア(Behavior)の使い方を覚えようぜ?」

kifuwarabe-futsu.png
「 Unity にも Godot にもあるだろ」

ohkina-hiyoko-futsu2.png
「 WPFは けっこうコードを書かなくちゃいけないのよ」

using System.Windows.Interactivity;

ramen-tabero-futsu2.png
「 👆 こんなネームスペース、どこにあるんだぜ?」

📖 【WPF】System.Windows.Interactivityがない!

ohkina-hiyoko-futsu2.png
「 有るんだろうけど、細かなこと覚えるより Livet 使った方がよくない?」

ramen-tabero-futsu2.png
「 👆 LivetCask を NuGet でインストールしたぜ」

20210830pg149a1.png

ramen-tabero-futsu2.png
「 👆 よし、ここまで来た」

using Microsoft.Xaml.Behaviors;
using System;
using System.Windows;
using System.Windows.Controls;

namespace WpfBehaviorPractice
{
    /// <summary>
    /// ボタンを押したら、警告ダイアログボックスが出るという振る舞い
    /// </summary>
    public class AlertBehavior : Behavior<Button>
    {
        public AlertBehavior()
        {
        }

        /// <summary>
        /// ダイアログボックスに表示する任意の文字列です
        /// </summary>
        public string Message
        {
            get { return (string)GetValue(MessageProperty); }
            set { SetValue(MessageProperty, value); }
        }

        /// <summary>
        /// Using a DependencyProperty as the backing store for Message.  This enables animation, styling, binding, etc...
        /// </summary>
        public static readonly DependencyProperty MessageProperty =
            DependencyProperty.Register("Message", typeof(string), typeof(AlertBehavior), new UIPropertyMetadata(null));

        /// <summary>
        /// 要素にアタッチされたときの処理。大体イベントハンドラの登録処理をここでやる
        /// </summary>
        protected override void OnAttached()
        {
            base.OnAttached();
            this.AssociatedObject.Click += Alert;
        }

        /// <summary>
        /// 要素にデタッチされたときの処理。大体イベントハンドラの登録解除をここでやる
        /// </summary>
        protected override void OnDetaching()
        {
            base.OnDetaching();
            this.AssociatedObject.Click -= Alert;
        }

        /// <summary>
        /// メッセージが入力されていたらメッセージボックスを出す
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Alert(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(this.Message))
            {
                return;
            }

            MessageBox.Show(this.Message);
        }
    }
}

ramen-tabero-futsu2.png
「 👆 なんだか分かってないが だいたい コピー貼り付けだぜ」

20210830pg150.png

ramen-tabero-futsu2.png
「 👆 ウィンドウのど真ん中に 適当にボタン置いて」

20210830pg151.png

ramen-tabero-futsu2.png
「 👆 Assets から Button の上へ Behavior をドラッグ&ドロップすればいいらしいんだが、無いぜ Assets とか」

<Window x:Class="WpfBehaviorPractice.MainWindow"
        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:WpfBehaviorPractice"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Button Content="適当に置いたボタン" HorizontalAlignment="Left" Height="72" Margin="323,202,0,0" VerticalAlignment="Top" Width="130"/>
    </Grid>
</Window>

ramen-tabero-futsu2.png
「 👆 じゃあ .xaml いじるか。これを、」

<Window x:Class="WpfBehaviorPractice.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfBehaviorPractice"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Button Content="適当に置いたボタン" HorizontalAlignment="Left" Height="72" Margin="323,202,0,0" VerticalAlignment="Top" Width="130">
            <i:Interaction.Behaviors>
                <local:AlertBehavior Message="こんにちは世界"/>
            </i:Interaction.Behaviors>
        </Button>
    </Grid>
</Window>

ramen-tabero-futsu2.png
「 👆 こうすりゃいいんだろ。 `xmlns:i="~" が増えてるの注意な」

20210830pg152.png

ramen-tabero-futsu2.png
「 👆 あっ、ボタンを押したら ダイアログボックス出たぜ」

kifuwarabe-futsu.png
「 ボタンだって マウスクリックをするというイベントもあれば マウスカーソルが重なるというイベントもあるだろ。
その書き方だと ボタンを押したときの振る舞いしか 書けなくないかだぜ?」

ramen-tabero-futsu2.png
「 どうすんだろな?」

ohkina-hiyoko-futsu2.png
「 Trigger というのがあるらしいんだけど、難しそうだから 今はパスね」

アクション(Action)

20210830pg153.png

ramen-tabero-futsu2.png
「 👆 LivetCask を NuGet でインストールするって言って 何のことだか 通じているんだろうか?」

kifuwarabe-futsu.png
「 こんな分かりづらい UIデザインが悪いぜ」

using Microsoft.Xaml.Behaviors;
using System.Windows;
using System.Windows.Controls;

namespace WpfActionPractice
{
    /// <summary>
    /// ボタンを押したら、警告ダイアログボックスが出るということ
    /// </summary>
    public class AlertAction : TriggerAction<Button>
    {
        /// <summary>
        /// ダイアログボックスに表示する任意の文字列です
        /// </summary>
        public string Message
        {
            get { return (string)GetValue(MessageProperty); }
            set { SetValue(MessageProperty, value); }
        }

        public static readonly DependencyProperty MessageProperty =
            DependencyProperty.Register("Message", typeof(string), typeof(AlertAction), new UIPropertyMetadata(null));

        public AlertAction()
        {
        }

        /// <summary>
        /// メッセージが入力されていたらメッセージボックスを出す
        /// </summary>
        /// <param name="o"></param>
        protected override void Invoke(object o)
        {
            if (string.IsNullOrEmpty(this.Message))
            {
                return;
            }

            MessageBox.Show(this.Message);
        }
    }
}

ramen-tabero-futsu2.png
「 👆 なんか コード量減ったな。 Behavior と似ているようでいて、ところどころ Behavior とも違うし」

20210830pg154.png

ramen-tabero-futsu2.png
「 👆 作業中の画面も載せとくか。今こんな感じの画面だぜ」

<Window x:Class="WpfActionPractice.MainWindow"
        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:WpfActionPractice"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Button Content="適当に置いたボタン" HorizontalAlignment="Left" Height="93" Margin="315,176,0,0" VerticalAlignment="Top" Width="134"/>
    </Grid>
</Window>

ramen-tabero-futsu2.png
「 👆 アクションをコーディングする前の、 .xaml はこんな感じ」

        xmlns:i="http://schemas.microsoft.com/xaml/behaviors"

ramen-tabero-futsu2.png
「 👆 ここに、これから xml namespace属性、つまり xmlns なんだが、 xmlns:i="~" を追加するのが 要点みたいだな」

<Window x:Class="WpfActionPractice.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfActionPractice"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Button Content="適当に置いたボタン" HorizontalAlignment="Left" Height="93" Margin="315,176,0,0" VerticalAlignment="Top" Width="134">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="Click">
                    <local:AlertAction Message="こんにちはアクション"/>
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </Button>
    </Grid>
</Window>

ramen-tabero-futsu2.png
「 👆 そしてこれが アクションをコーディングした .xaml だぜ」

kifuwarabe-futsu.png
「 あっ、 i:EventTrigger ってのがある! Trigger だぜ!
EventName="Click" って書いてるぜ!」

20210830pg155.png

ramen-tabero-futsu2.png
「 👆 ボタンをクリックしたら ダイアログボックスが出てきたぜ。 でけたな」

kifuwarabe-futsu.png
「 アクションの方が 多くの要望に応えられそうじゃないかだぜ? ビヘイビアって 仕事で出てくる要望をこなせるのかだぜ?」

ohkina-hiyoko-futsu2.png
「 そこらへんの ドメイン知識が インターネット上の記事にないのよね。
どう使い分けて 1つのアプリケーションを作り上げていくのか 見えないわねえ。 画面1つ作るだけなら できそうだけど」

Iコマンド(ICommand)

📖 WPF4.5入門 その57「コマンド」

ramen-tabero-futsu2.png
「 👆 コマンドも自習しようぜ?」

20210830pg156.png

ramen-tabero-futsu2.png
「 👆 NuGetを使って、LivetCask をインストール」

20210830pg157a1.png

ramen-tabero-futsu2.png
「 👆 とりあえず RoutedCommandプロパティを置いてみようぜ」

20210830pg158.png

ramen-tabero-futsu2.png
「 👆 画面は今こんな感じで」

<Window x:Class="WpfCommandPractice.MainWindow"
        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:WpfCommandPractice"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>

    </Grid>
</Window>

ramen-tabero-futsu2.png
「 👆 編集する前の .xaml はこんな感じで」

<Window x:Class="WpfCommandPractice.MainWindow"
        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:WpfCommandPractice"
        mc:Ignorable="d"
        Title="MainWindow" Height="143" Width="410">

    <Window.CommandBindings>
        <CommandBinding 
        Command="{x:Static local:MainWindow.AlertCommand}" 
        Executed="CommandBinding_Executed"
        CanExecute="CommandBinding_CanExecute"/>
    </Window.CommandBindings>

    <Grid>

    </Grid>
</Window>

ramen-tabero-futsu2.png
「 👆 編集した後の .xaml はこんな感じで」

20210830pg159.png

ramen-tabero-futsu2.png
「 👆 あれっ、エラーが出てる」

20210830pg160a1.png

ramen-tabero-futsu2.png
「 👆 じゃあ 足りてないとエラーが出てるやつを 先に追加したろ」

20210830pg161a1.png

ramen-tabero-futsu2.png
「 👆 そして CanExecuteチェックボックスと AlterCommandボタンを追加だぜ。
.xaml を直接編集するぜ」

<Window x:Class="WpfCommandPractice.MainWindow"
        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:WpfCommandPractice"
        mc:Ignorable="d"
        Title="MainWindow" Height="143" Width="410">

    <Window.CommandBindings>
        <CommandBinding 
        Command="{x:Static local:MainWindow.AlertCommand}" 
        Executed="CommandBinding_Executed"
        CanExecute="CommandBinding_CanExecute"/>
    </Window.CommandBindings>

    <Grid>
        <StackPanel>
            <CheckBox x:Name="checkBox" Content="CanExecute"/>
            <Button Content="AlertCommand" Command="{x:Static local:MainWindow.AlertCommand}" />
        </StackPanel>
    </Grid>
</Window>

20210830pg162a1.png

ramen-tabero-futsu2.png
「 👆 そして コメントアウトを解除」

using System.Windows;
using System.Windows.Input;

namespace WpfCommandPractice
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        /// <summary>
        /// ユーザーのアクションと、その処理を結びつけるもの
        /// </summary>
        public static RoutedCommand AlertCommand = new RoutedCommand();

        public MainWindow()
        {
            InitializeComponent();
        }

        private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            MessageBox.Show("Hello world");
        }

        private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = this.checkBox.IsChecked.Value;
        }
    }
}

20210830pg163.png

ramen-tabero-futsu2.png
「 👆 実行してみると ボタンは不活性で押せないな」

20210830pg164.png

ramen-tabero-futsu2.png
「 👆 チェックボックスをチェックすると ボタンが活性化したな」

20210830pg165.png

ramen-tabero-futsu2.png
「 👆 ボタンをクリックすると、なんか離れたところに ダイアログボックスが出てきたぜ。
このダイアログボックスは OKボタンを押して閉じないと、親ウィンドウは マウスで触れないぜ」

20210830pg166.png

ramen-tabero-futsu2.png
「 👆 チェックボックスを外すと また ボタンが不活性になったぜ」

kifuwarabe-futsu.png
「 CanExecute で活性/不活性を .xaml に書けるぐらいしか アクションと違いが分からないぜ」

ohkina-hiyoko-futsu2.png
「 コマンドにトリガーはあるの? 無いの?」

ramen-tabero-futsu2.png
「 分からないことだらけだな。 どう使い分けたらいいのか?」

ramen-tabero-futsu2.png
「 あれっ、記事に続きがあったぜ」

20210830pg167a1.png

ramen-tabero-futsu2.png
「 👆 コマンドには キー・バインディング もできるようだぜ。
ボタンが活性のとき ショートカット・キーで ボタン押せた。 これは コマンドだけだよな」

kifuwarabe-futsu.png
「 ビヘイビアと アクションと コマンドの良いところだけ 欲しい」

何度でもクリック!→

むずでょ@きふわらべ第29回世界コンピューター将棋選手権一次予選36位

光速のアカウント凍結されちゃったんで……。ゲームプログラムを独習中なんだぜ☆電王戦IIに出た棋士もコンピューターもみんな好きだぜ☆▲(パソコン将棋)WCSC29一次予選36位、SDT5予選42位▲(パソコン囲碁)AI竜星戦予選16位

Crieitは個人で開発中です。 興味がある方は是非記事の投稿をお願いします! どんな軽い内容でも嬉しいです。
なぜCrieitを作ろうと思ったか

また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!

こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください!

ボードとは?

むずでょ@きふわらべ第29回世界コンピューター将棋選手権一次予選36位 の最近の記事