XAMLを使わない状態でデータバインディングはどのようにするか調べてみました。
サンプルコード
プロジェクトの作成
dotnet new wpf -f net8.0 -n NoXAML02
cd NoXAML02
rm *.xaml
rm MainWindow.xaml.cs
ファイル名:NoXAML02.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UseWPF>true</UseWPF>
<!-- Main メソッドを持つ型を指定 -->
<StartupObject>NoXAML02.App</StartupObject>
</PropertyGroup>
</Project>
ファイル名:MainWindowViewModel.cs
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace NoXAML02;
public class MainWindowViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string? name = null) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
private int _count;
public int Count
{
get => _count;
set
{
if (_count == value) return;
_count = value;
OnPropertyChanged();
}
}
}
INotifyPropertyChangedを実装した普通のViewModel
ファイル名:CounterStackPanel.cs
using System.Windows;
using System.Windows.Controls;
namespace NoXAML02;
public class CounterStackPanel : StackPanel
{
// ① DependencyProperty の登録
public static readonly DependencyProperty CountProperty =
DependencyProperty.Register(
nameof(Count),
typeof(int),
typeof(CounterStackPanel),
new PropertyMetadata(0, OnCountChanged)
);
public int Count
{
get => (int)GetValue(CountProperty);
set => SetValue(CountProperty, value);
}
private readonly TextBlock _text;
private readonly Button _button;
public CounterStackPanel()
{
Orientation = Orientation.Horizontal;
Margin = new Thickness(10);
_text = new TextBlock { VerticalAlignment = VerticalAlignment.Center };
_button = new Button
{
Content = "+",
Width = 30,
Height = 30,
Margin = new Thickness(5, 0, 0, 0)
};
_button.Click += (_, __) => Count++;
Children.Add(_text);
Children.Add(_button);
UpdateText();
}
// ② DependencyProperty の変更コールバック
private static void OnCountChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((CounterStackPanel)d).UpdateText();
}
private void UpdateText()
{
_text.Text = $"Count: {Count}";
}
}
StackPanelを継承して、カウントアップされる値を表示するTextBlockとカウントアップするbuttonを配置したユーザーコントロール
ファル名:App.xaml.cs
using System.Windows;
using System.Windows.Data;
namespace NoXAML02;
partial class App : Application
{
// エントリポイント
[STAThread]
public static void Main()
{
var app = new App();
app.Startup += (sender, e) =>
{
// ① ViewModel を作成
var vm = new MainWindowViewModel { Count = 5 };
// ② カスタムコントロールを作成
var counter = new CounterStackPanel();
// ③ コントロールの CountProperty を ViewModel.Count に TwoWay バインド
var binding = new Binding(nameof(vm.Count))
{
Source = vm,
Mode = BindingMode.TwoWay
};
BindingOperations.SetBinding(counter, CounterStackPanel.CountProperty, binding);
// ④ Window を生成し、DataContext と Content に設定
var window = new Window
{
Title = "継承型カスタムコントロールのバインド例",
Width = 300,
Height = 150,
DataContext = vm,
Content = counter
};
window.Show();
};
app.Run();
}
}
実行イメージ
1.「+」ボタンを押します。
2.数値がカウントアップします。
解説
BindingOperations.SetBinding()メソッドは、バインディングオブジェクト(BindingBase)をターゲットオブジェクト(DependencyObject)とプロパティ(DependencyProperty)と関連付けします。
public static void BindingOperations.SetBinding(DependencyObject target, DependencyProperty dp, BindingBase binding);
バインディングオブジェクト
var binding = new Binding(nameof(vm.Count)) { Source = vm, Mode = BindingMode.TwoWay };
ターゲットオブジェクト
var counter = new CounterStackPanel();
StackPanelの継承内容は
Object→DependencyObject→UIElement→FrameworkElement→Panel→StackPanel
DependencyObjectの派生クラスであることが確認できる。
プロパティ
public class CounterStackPanel : StackPanel { // ① DependencyProperty の登録 public static readonly DependencyProperty CountProperty = DependencyProperty.Register( nameof(Count), typeof(int), typeof(CounterStackPanel), new PropertyMetadata(0, OnCountChanged) );
PropertyMetadataクラスは、依存関係プロパティの動作を定義するとのこと。
public PropertyMetadata (object defaultValue, System.Windows.PropertyChangedCallback propertyChangedCallback, System.Windows.CoerceValueCallback coerceValueCallback);
サンプルコードのdefaultValueは0になっています。coerceValueCallbackはOnCountChangedを指定しています。
OnCountChanged()→UpdateText()の順番で呼出されて、TextBlockのテキスト内容が変更されます。
MainWindowViewModelのCountプロパティの値が、バインドされることによってCounterStackPanelのTextBlockのテキストに連動するようになります。また、CounterStackPanelのButtonをクリックすることで加算された値がMainWindowViewModelのCountプロパティの値に連動します。
最後に
今回ビューモデルのプロパティとバインドすることが出来ました。次回はコマンドとバインディングする方法の記事にする予定です。
コメント