4/29 Codeを動作するように修正。Converterの追加。Git修正
※検証したらうまくいかなかったのでGitに上げます。
Github
git clone https://github.com/Sheephuman/MutiBingingTest.git
WPF Sheep Calender5日目 巻き角プロジェクトみたいな名前に替えようかな
この記事を参考に実装(実を言うと再現出来なかったのでChatGPTを使った。時間もないんで)
前回の関連記事
Ffmpegを使用したGUIフロントエンド「はるあこんば~た」を制作しています。
どうにも奥が深いので、原典でも漁るのがいいんでしょうかねえ。
実装は再現しやすいように出来るだけ単純化しています。
converterは使用せず、StringFormatだけで何とかします。
実装の概要
以下の実装はマルチバインディングを使用して、入力フォーマットを ${"-b:v{}k"}
とし、 それをターゲット コントロールに割り当てます。
<Window x:Class="MutiBingingTest.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:MutiBingingTest"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<!-- ユーザー入力のためのテキストボックス -->
<TextBox x:Name="textBoxInput" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="200" Height="30"
Text="{Binding UserInput, UpdateSourceTrigger=PropertyChanged}"/>
<!-- フォーマットされた文字列を表示するテキストブロック -->
<TextBlock HorizontalAlignment="Left" Margin="10,50,0,0" VerticalAlignment="Top" FontSize="16">
<TextBlock.Text>
<!-- 入力の順番を設定 {}:XAMLのエスケープシーケンスで、文字列のフォーマットを始めることを示します。
{0}:最初のバインドソースの値を挿入する場所 -->
<MultiBinding StringFormat="{}{1}">
<!-- ユーザー入力から更新されたStringBのバインド設定 -->
<Binding Path="UserInput" />
<!-- 何も表示しないが、更新トリガーのためにUserInputをバインド -->
<Binding Path="StringB"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</Grid>
</Window>
コードビハインド側
ユーザー入力用変数とそれを反映させる変数を用意します。
using System.ComponentModel;
using System.Windows;
namespace MutiBingingTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window,INotifyPropertyChanged
{
private string _userInput = string.Empty;
//ユーザー入力用文字列
public string UserInput
{
get => _userInput;
set
{
if (_userInput != value)
{
_userInput = value;
StringB = value; // ここを変更
OnPropertyChanged(nameof(UserInput));
//$"-b:v {value}"
}
}
}
// ビューモデルのプロパティを公開するためのプロパティ
private string _stringB = string.Empty;
// INotifyPropertyChangedのイベント
public event PropertyChangedEventHandler? PropertyChanged;
// StringBプロパティ
public string StringB
{
get { return _stringB; }
set
{
_stringB = $"-b:v {value}k";
OnPropertyChanged(nameof(StringB)); // 変更通知
}
}
public MainWindow()
{
InitializeComponent();
// ビューモデルまたはコードビハインドのプロパティに初期値を設定
DataContext = this;
}
// プロパティが変更された時にUIに通知するためのヘルパーメソッド
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
実行結果のGIF
実装内容は100%完璧に再現出来ることを保証します。
あとがき
気が向いたらもうちょっと高度な内容も書きます。
MainWindowのDataContextと混在させるとき
参考Link
コードビハインドで個別にmultiBindingを行う方法
GPT-4に何とか吐き出させました。これがなかったら数か月は掛かっている(;^ω^)
要するに全てコードビハインドで書く必要がある
メインサンプルのBranchを作成しました
git clone https://github.com/Sheephuman/WPFMaltiBindingTest_bySheephuman.git
外観
<Window x:Class="WPFMaltiBindingTest.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:WPFMaltiBindingTest"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<!-- Text box for user input -->
<TextBox x:Name="textBoxInput" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="200" Height="30"/>
<!-- Text block displaying formatted string -->
<TextBlock x:Name="textBlockOutput" HorizontalAlignment="Left" Margin="10,50,0,0" VerticalAlignment="Top" FontSize="16"/>
</Grid>
</Window>
コードビハインド
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace WPFMaltiBindingTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged
{
private string _userInput = string.Empty;
// ユーザー入力用文字列
public string UserInput
{
get => _userInput;
set
{
if (_userInput != value)
{
_userInput = value;
StringB = $"-b:v {_userInput}k";
OnPropertyChanged(nameof(UserInput));
}
}
}
private string _stringB = string.Empty;
// StringBプロパティ
public string StringB
{
get => _stringB;
set
{
if (_stringB != value)
{
_stringB = value;
OnPropertyChanged(nameof(StringB));
}
}
}
// INotifyPropertyChangedのイベント
public event PropertyChangedEventHandler? PropertyChanged;
public MainWindow()
{
InitializeComponent();
SetupBindings();
}
// バインディングのセットアップ
private void SetupBindings()
{
// ユーザー入力バインディング
Binding userInputBinding = new Binding("UserInput")
{
Source = this,
UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
};
textBoxInput.SetBinding(TextBox.TextProperty, userInputBinding);
// マルチバインディング設定
MultiBinding multiBinding = new MultiBinding()
{
StringFormat = "{0}"
};
multiBinding.Bindings.Add(new Binding("StringB") { Source = this, UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged });
textBlockOutput.SetBinding(TextBlock.TextProperty, multiBinding);
}
// プロパティが変更された時にUIに通知するためのヘルパーメソッド
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
こんな風に個別に書かないと動きません。
つまりXAML側とはインスタンスが別なのだと考えられます(かなり多分)
こうも書けますが、管理が煩雑になるのでDataContext = this
;のままで実装する事をおすすめします。基本的にXAML実装とは混在出来ません。どちらかが上書きされて意図通りに動作しない。
参考 マルチバインディングを使わない例
テキストボックスを2つ配置し、文字列を組み合わせて表示。
マルチバインディングに拘らない方が上手くいくらしい。
実際の実装ではComboBoxを使う。
github
git clone https://github.com/Sheephuman/WPFMaltiBindingTest_bySheephuman.git
実行結果
Code
using System.ComponentModel;
namespace WPFMaltiBindingTest
{
public class QueryField : INotifyPropertyChanged
{
private string _userInputA = string.Empty;
public string UserInputA
{
get => _userInputA;
set
{
if (_userInputA != value)
{
_userInputA = value;
OnPropertyChanged(nameof(UserInputA));
UpdateAllInput();
}
}
}
private string _userInputB = string.Empty;
public string UserInputB
{
get => _userInputB;
set
{
if (_userInputB != value)
{
_userInputB = value;
OnPropertyChanged(nameof(UserInputB));
UpdateAllInput();
}
}
}
private string _allInput = string.Empty;
public string AllInput
{
get => _allInput;
private set
{
if (_allInput != value)
{
_allInput = value;
OnPropertyChanged(nameof(AllInput));
}
}
}
//User入力文字列を足し合わせるメソッド
private void UpdateAllInput()
{
AllInput = $"-b:v {_userInputA}k -codec:v {_userInputB}";
}
public event PropertyChangedEventHandler? PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
コードビハインド
using System.ComponentModel;
using System.Windows;
namespace WPFMaltiBindingTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
// ビューモデルまたはコードビハインドのプロパティに初期値を設定
// DataContextにデータクラスをセット
var qf =new QueryField();
DataContext = qf;
}
}
}
XAML
<Window x:Class="WPFMaltiBindingTest.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:WPFMaltiBindingTest"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<!-- ユーザー入力のためのテキストボックス -->
<StackPanel>
<TextBox x:Name="UserInputA" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="200" Height="30"
Text="{Binding UserInputA, UpdateSourceTrigger=PropertyChanged}"/>
<TextBox x:Name="UserInputB" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="200" Height="30"
Text="{Binding UserInputB, UpdateSourceTrigger=PropertyChanged}"/>
<!-- フォーマットされた文字列を表示するテキストブロック -->
<TextBlock HorizontalAlignment="Left" Margin="10,50,0,0" VerticalAlignment="Top" FontSize="16"
Text="{Binding AllInput}">
</TextBlock>
</StackPanel>
</Grid>
</Window>
Converterを利用した方法
意外と簡単なので追記。コード量も短くて済む。
参考にしたページ。ChatGPTは調子が悪かった(笑)
Github
SampleCodeがダウンロード可能だが、古いのでこちらでも用意しておくことにする。
Visual Studio 2022 .net8を指定している
git clone https://github.com/Sheephuman/ConverterBindingTest.git
ViewModel
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConvertetBindingTest
{
// ViewModel.cs
public class ViewModel : INotifyPropertyChanged
{
private string _inputA = string.Empty;
public string InputA
{
get => _inputA;
set
{
_inputA = value;
OnPropertyChanged(nameof(InputA));
}
}
private string _inputB = string.Empty;
public string InputB
{
get => _inputB;
set
{
_inputB = value;
OnPropertyChanged(nameof(InputB));
}
}
public event PropertyChangedEventHandler? PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Converterを実装
using System.Globalization;
using System.Windows.Data;
namespace ConvertetBindingTest
{
// YourConverter.cs
public class SheepConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
string result = string.Empty;
result = values[0].ToString() + values[1].ToString()
+ values[2].ToString();
return result;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
XAMLの実装
<Window x:Class="ConvertetBindingTest.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:ConvertetBindingTest" d:DataContext="{d:DesignInstance Type=local:ViewModel}"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<local:ViewModel/>
</Window.DataContext>
<!-- コンバーター -->
<Grid>
<Grid.Resources>
<local:SheepConverter x:Key="SheepConverter"/>
</Grid.Resources>
<TextBlock Height="50" Background="AliceBlue">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource SheepConverter}">
<Binding Path="Text"
ElementName="Input1"/>
<Binding Path="Text"
ElementName="Input2" />
<Binding Path="Text"
ElementName="Input3" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<StackPanel Orientation="Vertical">
<TextBox x:Name="Input1"
Grid.Column="0"
HorizontalAlignment="Stretch"
Margin="2,0"/>
<TextBox x:Name="Input2"
Grid.Column="1"
HorizontalAlignment="Stretch"
Margin="2,0" />
<TextBox x:Name="Input3"
Grid.Column="2"
HorizontalAlignment="Stretch"
Margin="2,0" />
</StackPanel>
</Grid>
</Window>