泥庭

2014年12月5日

DataGridとDataTable(その2)【WPF編】

Filed under: .NET, WPF — タグ: , — yone64 @ 9:39 PM

さて、前回の続き。
DataGridとDataTableは非常に相性がよいのですが、「AutoGenerateColumn=”True”」で使う際困った問題があります。
例えば、前回のソースコードを、次のように変更してみます。

var table = new DataTable();
table.Columns.Add("Column1");
table.Columns.Add("Column.2");
table.Columns.Add("Column3");
table.Columns.Add("Column4");

for (int i = 0; i < 20; i++)
{
    var row = table.NewRow();
    for (int j = 0; j < table.Columns.Count; j++)
    {
        row[j] = "[" + (i + 1) + "," + (j + 1) + "]";
    }
    table.Rows.Add(row);
}
this.DataContext = table;

違いは、Column名に「.」が入ったこと。そうすると
キャプチャ
あら不思議。表示されなくなりました。
これは各方面で報告されている(例えば、ココとかココとか)通り で、WPFのBindingPathの解決の仕組みとColumn名がバッティングしているためです。

回避方法として、提案されているのは

  • Unicodeで似ている文字を使う
  • BindingPathを”[ ]”で括る

です。Unicodeで似ている文字を使うのは論外として、”[ ]”で括るのは、このケースにおいては、一見うまくいっているように見えます。しかし、WPFのBindingPathの予約文字と被ったってことは、考慮しないといけないのは”.”だけではないことは容易に想像できますね。例えば、”[“とかがColumn名に含まれると非常にまずいわけです。(この場合は問答無用で例外が発生します。)

つまり、ClassのProperty名と違って使用不可文字のないDataColumn.ColumnNameを使用してBindingPathとすることはそもそも無理があるということになります。

ひとまず、自分の回避策を載せておきますが、どうするのが正解かはわかっていません。

var table = new DataTable();
table.Columns.Add(new DataColumn { ColumnName = "C" + table.Columns.Count, Caption = "Column1" });
table.Columns.Add(new DataColumn { ColumnName = "C" + table.Columns.Count, Caption = "Column.2" });
table.Columns.Add(new DataColumn { ColumnName = "C" + table.Columns.Count, Caption = "Column3" });
table.Columns.Add(new DataColumn { ColumnName = "C" + table.Columns.Count, Caption = "Column4" });

for (int i = 0; i < 20; i++)
{
    var row = table.NewRow();
    for (int j = 0; j < table.Columns.Count; j++)
    {
        row[j] = "[" + (i + 1) + "," + (j + 1) + "]";
    }
    table.Rows.Add(row);
}
this.DataContext = table;
private void DataGrid_OnAutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
    var column = e.Column as DataGridBoundColumn;
    var view = (sender as DataGrid).ItemsSource as DataView;
    if (view != null && column != null)
    {
        var dataColumn =
            view.Table.Columns.OfType<DataColumn>().FirstOrDefault(c => c.ColumnName == e.PropertyName);
        if (dataColumn != null)
        {
            column.Header = dataColumn.Caption;
        }
    }
}

ColumnNameは絶対にBindingPathの予約文字とかぶらないように、プログラム的に一意の文字列をふります。そして、Headerに表示したい文字列はCaptionを利用。その後、Column作成時にCaptionをHeaderに代入します。

キャプチャ

ちなみに、DataTableはIndexで各ColumnとBindingができるので、そもそもAutoGenerateColumnでBindingを作成する際は、Indexでの解決にしてくれればよかったのに。。。と思ったりしないわけでもないのですが、どうなんでしょう。
# もしくは、BindingはColumnNameを使って、表示はCaptionを使うとか。

広告

2013年12月21日

WPFだからできること

Filed under: .NET, WPF — タグ: , , , — yone64 @ 6:58 PM

この記事は、XAML Advent Calendarの第21日目の記事です。

WPFは、XAMLでUIを定義するプラットフォームの始祖にしてもっとも強力な機構を持っています。
そこで、他プラットフォームでは使えないWPFならではの記述を2つ紹介します。

  1. SetterでBindingを設定
  2. DataTemplateでDataTypeを指定

サンプルコードはこちら

<Window x:Class="WpfSample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:vm="clr-namespace:WpfSample.ViewModels"
        Title="MainWindow" Height="200" Width="250">
    <Grid>
        <ItemsControl ItemsSource="{Binding}">
            <ItemsControl.Resources>
                <DataTemplate DataType="{x:Type vm:ButtonViewModel}">
                    <Button Content="{Binding Text}" Command="{Binding Command}"></Button>
                </DataTemplate>
                <DataTemplate DataType="{x:Type vm:TextBoxViewModel}">
                    <TextBox Text="{Binding Text}"/>
                </DataTemplate>
            </ItemsControl.Resources>
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <Canvas></Canvas>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemContainerStyle>
                <Style>
                    <Setter Property="Canvas.Left" Value="{Binding Left}"/>
                    <Setter Property="Canvas.Top" Value="{Binding Top}"/>
                    <Setter Property="Canvas.Width" Value="{Binding Width}"/>
                    <Setter Property="Canvas.Height" Value="{Binding Height}"/>
                </Style>
            </ItemsControl.ItemContainerStyle>
        </ItemsControl>
    </Grid>
</Window>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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;
using WpfSample.ViewModels;

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

            var text = new TextBoxViewModel { Left = 10, Top = 10, Width = 200, Height = 20, Text = "Hello" };
            var button = new ButtonViewModel 
            {
                Left = 90,
                Top = 120,
                Width = 100,
                Height = 20, 
                Text = "Click",
                Command = new RelayCommand(new Action(()=> MessageBox.Show(text.Text))),
            };

            this.DataContext = new List<ViewModelBase> { text, button};
        }
    }
}

実行結果はこちら。

キャプチャ3

まずひとつ目から、
ItemsControl.ItemsContainerSytleで定義しているように、StyleのSetterのValueにBindingを指定することができます。
これにより、ContentControlの各プロパティーとViewModelのプロパティーのBindingが可能になります。これはなかなか便利なのでおすすめ。
よく使うのは、上の例のように、ItemPanel上での位置情報をBindingで指定したり、あとはListBoxのIsSelectedをViewModel側で参照したいときとかでしょうか。

ふたつ目は、DataTemplateのDataTypeプロパティー。
このプロパティーを利用すると、ViewModelの型で適用するStyleを切り替えることができます。上の例では、TextBoxとButtonのコントロールをViewModelの型で指定しています。
動的に画面の構成が変わる画面を作成する場合は、ひとつ目のBindingの仕組みと組み合わせると便利です。

以上、他のXAMLプラットフォームからWPFに来た方は参考にしてみてください。

※ItemsControlのBinding周りは、XAML Advent Calendar 1日目のぐらばくさんのエントリーが非常にくわしいです。

なお、ソースコードの全体は、githubに上げてあります。

2010年11月4日

Bindingの状況をTraceする

Filed under: WPF — タグ: , , — yone64 @ 3:34 AM

WPFのBindingをトレースすることが出来ることを知りました。
詳細はこちら

XAML

<Window x:Class="WpfApplication5.Window4"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
        Title="Window4" Height="300" Width="300">
    <Grid>
        <TextBox Height="24" HorizontalAlignment="Left" Margin="62,108,0,0" Name="textBox1" VerticalAlignment="Top" Width="120"
                 Text="{Binding Path=Text, diag:PresentationTraceSources.TraceLevel=High}"/>
    </Grid>
</Window>

いつもと違うのは

xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"



diag:PresentationTraceSources.TraceLevel=High

です。

その他、VisualStudio2010で出力するためには、[ツール]→[オプション]より、下記の設定が必要です。

image

コード上でBindingを設定・取得する

Filed under: WPF — タグ: , , — yone64 @ 3:18 AM

例によって覚書

XAMLで書くと

<TextBox Height="24"  Width="120" Text="{Binding Path=Name}"/>

コードで書くと

//設定
Binding binding = new Binding("Name");
textBox1.SetBinding(TextBox.TextProperty, binding);

//BindingOperationsクラスでも可
BindingOperations.SetBinding(textBox1, TextBox.TextProperty, binding);

//取得
//コントロールから取得可能なのはBindingExpression
BindingExpression bindingExpresson = textBox1.GetBindingExpression(TextBox.TextProperty);
Binding binding2 = bindingExpresson.ParentBinding;

//BindingOperationsクラスでも可
binding2 = BindingOperat

WordPress.com Blog.