DOBON.NET DOBON.NETプログラミング掲示板過去ログ

WPFでチェックボックスを.csだけで作成するにはどうしたらよいですか?

環境/言語:[WindowsXP SP3 .NET3.5 VCS2008SP1]
分類:[.NET]

質問です。WPFでチェックボックスを.csだけで作成するにはどうしたらよいですか?
Formなら次のように書けます。

CheckBox[] checkBox;
string[] lines = {"りんご", "柿"};
checkBox = new CheckBox[lines.Length];
for (int i = 0; i < lines.Length; i++)    {
    this.checkBox[i] = new System.Windows.Forms.CheckBox();
    this.checkBox[i].AutoSize = true;
    this.checkBox[i].Location = new System.Drawing.Point(20, 2 + i * 21);
    YSize = 36 + i * 20 +36;
    this.checkBox[i].Name = "checkBox[i]";
    this.checkBox[i].Size = new System.Drawing.Size(200, 16);
    this.checkBox[i].Text = lines[i];
    this.Controls.Add(this.checkBox[i]);
}


WPFの本を参考に、次のようにしてみました。
string[] lines = {"りんご", "柿"};
StackPanel panel = new StackPanel();
foreach (string line in lines) {
CheckBox checkbox = new CheckBox();
checkbox.Content = line;
panel.Children.Add(checkbox);
}

Window Window1 = new Window();
Window1.Content = panel;

Application application = new Application();
application.Run(Window1);

ウィンドウは開くもののなにも表示されず、途方にくれています。
アドバイスいただけないでしょうか。
コード的には全く正しいですね。
私の環境ではちゃんとチェックボックスが表示されます(ウィンドウ左上隅にひっそりと)。
まず、ブレークポイントなどでちゃんとそのコードの部分を通っているかどうかを確認してみてください。
■No25817に返信(Hongliangさんの記事)
Hongliangさま、早速ご回答ありがとうございました。
コード的には正しいとのこと。とするとその前後で間違っているのでしょうか。

Window1.xmal.csの全コードは次のようになっています。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfApplication1
{
    /// <summary>
    /// Window1.xaml の相互作用ロジック
    /// </summary>
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();

            string[] lines = { "りんご", "柿" };
            StackPanel panel = new StackPanel();
            foreach (string line in lines)
            {
                CheckBox checkbox = new CheckBox();
                checkbox.Content = line;
                panel.Children.Add(checkbox);
            }

            Window Window1 = new Window();
            Window1.Content = panel;

            //Application application = new Application();
            //application.Run(Window1);
        }
    }
}

さきほど、これでウィンドウは開くと書いたのですが、それは、
            //Application application = new Application();
            //application.Run(Window1);
の2行をコメントアウトした、現在の場合でした。
この2行があると、
アセンブリ 'WpfApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' で定義された 'Window1' のインスタンスを作成できません。 
呼び出しのターゲットが例外をスローしました。  
マークアップ ファイル 'Window1.xaml' 行 1 位置 9 にエラーがあります。
というエラーが出てしまいます。

application.Runというのは、WPFでないC#のほうでは、
Program.csファイルのmainに書くと思うので、
このコードはすべてmainに書けばよいと思うのですが、
WPFの場合は、ソリューションエクスプローラーのなかにProgram.csが存在しないので、
とりあえずWindow1.xaml.csに書いています。
そのあたりが間違っている気がするのですが、
どう修正したらよいのか、よくわからないのです。
App.xaml の StartupUri に指定されている xaml ファイルの Window クラスが、アプリケーション開始時に表示される Window になります。

> Window Window1 = new Window();
> Window1.Content = panel;
で、その中で新しい Window を作っても、上述の Window とは別物なので、それに対して何しようが初めに表示される Window には何の影響もありません。
// new キーワードは、文字通り「新しい」オブジェクトを作成するものです。

自分の Content プロパティに対して設定するなら、
this.Content = ...
という記述をしなければなりません。

// しかし、なぜわざわざコードで追加するのでしょうか? 殆どのことは xaml 及びバインディングで解決できると思うのですが。
string[] lines = { "りんご", "柿" };
StackPanel panel = new StackPanel();

foreach (string line in lines)
{
    CheckBox checkbox = new CheckBox();
    checkbox.Content = line;
    panel.Children.Add(checkbox);
}

this.Content = panel;

として解決しました。ありがとうございました。

さらに引き続き疑問点が残ります。
おっしゃるとおりで、WPFは(それほど詳しいとはとうていいえないのですが)、
xamlとバインディングでいろいろプログラムを書くようです。

xamlは、xmlに似ているので、固定的なものを書くのには都合がよいようです。
で、今回なぜcsを使いxamlを使わないかというと、このソフトはチェックリストの試作品であるためです。
linesは実際にはファイルから読むので、不特定多数なのでxamlには書けないのです。
string[] lines = { "りんご", "柿" };
のこともあるし、
string[] lines = { "りんご", "柿", "ぶどう", "バナナ" };
のこともあるわけです。

こういう作業にはどうやらバインディングを使うらしい、
そういうことならバインディングだ、ということは、
WPFの本を4冊脇において眺めていてわかったのですが、
まだバインディングの基本を理解できておらず、使えないためです…。

今回バインディングを理解しながら書くのもありだと思うのですが…。
いまのところ上記のようにぜんぜん理解できていないので、
書くとすると、ぜんぶ教えてになっちゃいます…。

さて、それで疑問点というと、この位置の調整です。
このpanelの位置をウィンドウ内のたとえば左から50ピクセル、
上から30ピクセルに表示するにはどうしたらよいでしょう?

panel.Leftとか、panel.Topとかのプロパティがあるのかと思ったら、見当たりませんでした。

それではxmalでと思って、StackPanelを配置して、
<StackPanel Margin="50,30,12,98" Name="stackPanel1" />

string[] lines = { "りんご", "柿" };
//StackPanel panel = new StackPanel();

foreach (string line in lines)
{
    CheckBox checkbox = new CheckBox();
    checkbox.Content = line;
    //panel.Children.Add(checkbox);
    stackPanel1.Children.Add(checkbox);
}

this.Content = stackPanel1;

としたところ、
この最後の行で、
アセンブリ 'WpfApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' で定義された 'Window1' のインスタンスを作成できません。 呼び出しのターゲットが例外をスローしました。  マークアップ ファイル 'Window1.xaml' 行 1 位置 9 にエラーがあります。
とエラーになっています。

できれば配置はレイアウトを自由に設定できるxamlでと考えているので、
csにこだわらずアドバイスいただければと。
重ねてですみませんが、よろしくお願いします。
同じ UI を繰り返す場合は ItemsControl 及びその派生クラス(ListBox など)を使うことを知っていれば割とどうとでもなります。

> このpanelの位置をウィンドウ内のたとえば左から50ピクセル、
> 上から30ピクセルに表示するにはどうしたらよいでしょう?
> panel.Leftとか、panel.Topとかのプロパティがあるのかと思ったら、見当たりませんでした。
WPF では、配置は親要素の Padding、自身の Margin などによって決定されます。
詳しくは MSDN の「配置、余白、パディングの概要」に記事があります。
まず、全体の要素の配置を考えてください。
そしてそれらの要素の親子関係を考えて、適切なパネルを選択していきましょう。

> this.Content = stackPanel1;
Window1.xaml の Window の子要素として StackPanel を配置したんですよね?
それなら this であるところの Window1 の Content は既に stackPanel1 が設定されています。再代入する意味はありません。
XAML のエラーの方は、これだけではなんとも言い難いですね。


// WinForm 使ったら? と言いたいところですけど。
■No25823に返信(Hongliangさんの記事)
できました。ありがとうございました。
string[] lines = { "りんご", "柿" };

foreach (string line in lines)
{
CheckBox checkbox = new CheckBox();
checkbox.Content = line;
stackPanel1.Children.Add(checkbox);
}

だけでオーケーでした。

おっしゃるとおり、Formで同じものはすでに作っていて、
いわばWPFへの移植中という感じです。
WPFのほうが、表現力が豊かなので、
どうしてもWPFで、と考えています。
解決済み!

DOBON.NET | プログラミング道 | プログラミング掲示板