【C# WPF】Xaml で定義したコントロールを初期化子で参照する

今回はC# WPF でxaml ファイルで定義したコントロールを初期化子で参照する方法について書きます。

結論を先に言うと、初期化子で直接参照することはできません

代替案として、FindNameメソッドでコントロール名からコントロールを検索するようにしました

やりたいこと

上記ウィンドウにコンボボックスが2つあります。

<Window x:Class="Test_Static.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:Test_Static" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800" DataContext="x:Static">
    <Grid>
        <ComboBox x:Name="Combo1" HorizontalAlignment="Left" Margin="45,25,0,0" VerticalAlignment="Top" Width="140" Height="30"/>
        <ComboBox x:Name="Combo2" HorizontalAlignment="Left" Margin="45,70,0,0" VerticalAlignment="Top" Width="140" Height="30"/>
    </Grid>
</Window>

xamlファイルは上記のとおりです。

ここで、ComboBoxのx:Name 属性にそれぞれCombo1、Combo2と設定しました

そのx;Name属性を設定したコントロールを、csファイルの初期化子で参照できるようにしたいです。

using System;
using System.Windows;
using System.Windows.Controls;

namespace Test_Static
{
    // MainWindow.xaml の相互作用ロジック
    public partial class MainWindow : Window
    {
        // コンボボックス設定リストのクラス
        private class TestComboSet
        {
            public String[] list;
            public int id;
            public ComboBox combo;
        }

        // コンボボックス設定リストのオブジェクト
        private TestComboSet[] testComboSet = new TestComboSet[]
        {
            new TestComboSet()
            {
                list = new String[]{"A","B","C"},
                id = 1,
                /*** ここでXamlで定義したコンボボックスを参照したい ***/
            },
            new TestComboSet()
            {
                list = new String[]{"D","E","F"},
                id = 2,
                /*** ここでXamlで定義したコンボボックスを参照したい ***/
            }
        };

        public MainWindow()
        {
            InitializeComponent();

            foreach( TestComboSet testComboSet in testComboSet )
            {
                /*** ここでコンボボックスにリストを設定する ***/
            }
        }
    }
}
  • MainWindowのフィールドに、TestComboSet型の配列testComboSetを定義します。
  • testComboSetの初期化子でXaml で定義したコンボボックスを参照します
  • コンストラクタで、コンボボックスにリストを設定をしたいです
開発環境
  • Windows10
  • Microsoft Visual Studio Community2019
  • .NET Framework 4.7.2
  • WPF アプリ( .NET Framework )

初期化子でコントロールの参照はできない

単純に初期化子でコントロールの参照をする場合は下記のようになります。しかし、この方法だとエラーになります

        // コンボボックス設定リストのオブジェクト
        private TestComboSet[] testComboSet = new TestComboSet[]
        {
            new TestComboSet()
            {
                list = new String[]{"A","B","C"},
                id = 1,
                combo = Combo1 // ★★★ エラー
            },
            new TestComboSet()
            {
                list = new String[]{"D","E","F"},
                id = 2,
                combo = Combo2 // ★★★ エラー
            }
        };

エラー内容は、

エラー CS0236 フィールド初期化子は、静的でないフィールド、メソッド、またはプロパティ ‘MainWindow.Combo1’ を参照できません

プログラミングガイドには以下の記述があります。

フィールドの初期化子は、他のインスタンス フィールドを参照できません。

どうやら初期化子では、参照できないようです。

コンストラクタで初期化することは可能です

        // コンボボックス設定リストのオブジェクト
        private TestComboSet[] testComboSet = new TestComboSet[]
        {
            new TestComboSet()
            {
                list = new String[]{"A","B","C"},
                id = 1,
            },
            new TestComboSet()
            {
                list = new String[]{"D","E","F"},
                id = 2,
            }
        };

        public MainWindow()
        {
            InitializeComponent();


            // ★★★ 初期化の設定が2か所になる
            testComboSet[0].combo = Combo1; // OK
            testComboSet[1].combo = Combo2; // OK

            foreach ( TestComboSet testComboSet in testComboSet )
            {
                
            }
        }

でも、初期化子とコンストラクタで2か所で初期化することになります。保守性の観点で、初期化は1か所に固めておきたいところです。

FindName メソッドで参照する

そこで、初期化子ではコントロール名を記載して、コンストラクタでコントロールを参照する方針に変更します

FindName メソッドを使うとコントロール名からコントロールを検索することができます。

なので、初期化子ではコントロール名を定義して、コンストラクタでコントロールを検索と設定をします

具体的に↓のコードのようになります。

 

using System;
using System.Windows;
using System.Windows.Controls;

namespace Test_Static
{
    // MainWindow.xaml の相互作用ロジック
    public partial class MainWindow : Window
    {
        // コンボボックス設定リストのクラス
        private class TestComboSet
        {
            public String[] list;
            public int id;
            public String combo_name; // ★★★ 追加
            public ComboBox combo;
        }

        // コンボボックス設定リストのオブジェクト
        private TestComboSet[] testComboSet = new TestComboSet[]
        {
            new TestComboSet()
            {
                list = new String[]{"A","B","C"},
                id = 1,
                combo_name = "Combo1" // コントロール名
            },
            new TestComboSet()
            {
                list = new String[]{"D","E","F"},
                id = 2,
                combo_name = "Combo2" // コントロール名
            }
        };

        public MainWindow()
        {
            InitializeComponent();

            foreach ( TestComboSet testComboSet in testComboSet )
            {
                // ★★★ コントロール名からコントロールを取得
                testComboSet.combo = FindName(testComboSet.combo_name) as ComboBox;

                // コントロールにリストを設定
                foreach( String s in testComboSet.list)
                {
                    testComboSet.combo.Items.Add(s);
                }
            }
        }
    }
}
  • TestComboBoxクラスにコントロール名のフィールドを追加します。
  • testComboSetの初期化子でコントロール名を設定します。
  • コンストラクタでFindNameメソッドで、コントロール名からコントロールを検索し設定します。

この方法だと、初期化が初期化子のみでできます。1か所でできたので、すっきり書けます。

コードの実行結果

コンボボックスの参照ができて、リストの設定もできました。

まとめ

今回はC# WPF でxaml ファイルで定義したコントロールを初期化子で参照する方法について書きました。

初期化子で直接参照することはできません。

FindNameメソッドでコントロール名からコントロールを検索しました。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です