xaml
データバインディング
サーチ…
構文
<TextBlock Text="{Binding Title}"/><TextBlock Text="{Binding Path=Title}"/><TextBlock> <TextBlock.Text> <Binding Path="Title"/> </TextBlock.Text> </TextBlock>
備考
これらのタグはすべて同じ結果を生成します。
テキストプロパティに文字列をバインドする
実行時にUIコンテンツを変更するには、 Bindingを使用できます。バインドされたプロパティがコードから変更されると、UIに表示されます。
<TextBlock Text="{Binding Title}"/>
変更をUIに通知するには、プロパティがPropertyChangedイベントをINotifyPropertyChangedインターフェイスからINotifyPropertyChangedか、 Dependency Propertyを使用する必要があります。
プロパティ "Title"がxaml.csファイルまたはXAML Datacontextクラスにある場合、バインディングが機能しています。
DatacontextはXAMLで直接設定できます
<Window x:Class="Application.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Application">
<Window.DataContext>
<local:DataContextClass/>
</Window.DataContext>
文字列バインディングの書式設定
日付のような何かのバインドを行うときには、コード内で日付を特定しないで特定の形式で表示することができます。
これを行うには、StringFormatプロパティを使用できます。
ここではいくつかの例を示します。
Text="{Binding Path=ReleaseDate, StringFormat=dddd dd MMMM yyyy}"
これは私の日付を次のようにフォーマットします:
2016年8月16日火曜日
温度の別の例がここにあります。
Text="{Binding Path=Temp, StringFormat={}{0}°C}"
この形式は次のとおりです。
25℃
INotifyPropertyChangedの基本
静的オブジェクトを表示したいだけでなく、関連するオブジェクトの変更にUIが応答するようにするには、 INotifyPropertyChangedインタフェースの基本を理解する必要があります。
MainWindow次のように定義されていると仮定します。
<Window x:Class="Application.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:vm="clr-namespace:Application.ViewModels>
<Window.DataContext>
<vm:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<TextBlock Text={Binding Path=ApplicationStateText}" />
</Grid>
</Window>
私たちのViewModelクラスでMainWindowViewModelとして定義
namespace Application.ViewModels
{
public class MainWindowViewModel
{
private string _applicationStateText;
public string ApplicationStateText
{
get { return _applicationStateText; }
set { _applicationStateText = value; }
}
public MainWindowViewModel()
{
ApplicationStateText = "Hello World!";
}
}
}
アプリケーションのTextBlockは、そのバインディングのためにテキストHello Worldを表示します。実行時にApplicationStateTextが変更された場合、UIにはそのような変更が通知されません。
これを実装するには、DataSource、この場合はMainWindowViewModelは、インタフェースINotifyPropertyChangedを実装する必要があります。これにより、 BindingsがPropertyChangedEventに登録できるようになります。
ApplicationStateTextプロパティを次のように変更するたびに、 PropertyChangedEventHandlerを呼び出すだけです。
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace Application.ViewModels
{
public class MainWindowViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged( [CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private string _applicationStateText;
public string ApplicationStateText
{
get { return _applicationStateText; }
set
{
if (_applicationStateText != value)
{
_applicationStateText = value;
NotifyPropertyChanged();
}
}
}
public MainWindowViewModel()
{
ApplicationStateText = "Hello World!";
}
}
}
TextBlock.Text Bindingが実際にPropertyChangedEventリッスンすることを確認してください。
<Window x:Class="Application.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:vm="clr-namespace:Application.ViewModels">
<Window.DataContext>
<vm:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<TextBlock Text={Binding Path=ApplicationStateText, UpdateSourceTrigger=PropertyChanged }" />
</Grid>
</Window>
INotifyPropertyChangedおよびINotifyCollectionChangedを使用してオブジェクトのコレクションにバインドする
Userオブジェクトのプロパティをプログラムで更新できるViewModel Usersプロパティの下にリストされているすべてのUserオブジェクトを表示するはずのListViewがあるとします。
<ListView ItemsSource="{Binding Path=Users}" >
<ListView.ItemTemplate>
<DataTemplate DataType="{x:Type models:User}">
<StackPanel Orientation="Horizontal">
<TextBlock Margin="5,3,15,3"
Text="{Binding Id, Mode=OneWay}" />
<TextBox Width="200"
Text="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Delay=450}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Userオブジェクトに対してINotifyPropertyChanged Beeingが正しく実装されているにもかかわらず
public class User : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private int _id;
private string _name;
public int Id
{
get { return _id; }
private set
{
if (_id == value) return;
_id = value;
NotifyPropertyChanged();
}
}
public string Name
{
get { return _name; }
set
{
if (_name == value) return;
_name = value;
NotifyPropertyChanged();
}
}
public User(int id, string name)
{
Id = id;
Name = name;
}
private void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
あなたのViewModelオブジェクト用
public sealed class MainWindowViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private List<User> _users;
public List<User> Users
{
get { return _users; }
set
{
if (_users == value) return;
_users = value;
NotifyPropertyChanged();
}
}
public MainWindowViewModel()
{
Users = new List<User> {new User(1, "John Doe"), new User(2, "Jane Doe"), new User(3, "Foo Bar")};
}
private void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
ユーザーの変更がプログラムによって行われた場合、UIは更新されません。
これは、単にList自体のインスタンスにINotifyPropertyChangedを設定しているためです。エレメントの1つのプロパティーが変更された場合にのみ、リストを完全に再インスタンス化した場合のみ、UIが更新されます。// DO NOT DO THIS
User[] userCache = Users.ToArray();
Users = new List<User>(userCache);
しかし、これは非常に面倒で、信じられないほど性能が悪いです。
ユーザーのIDと名前の両方を示す100,000の要素の一覧がある場合、それぞれを再作成する必要がある場所に200,000のデータバインディングがあります。これにより、何かが変更されたときはいつでもユーザーに遅れをとらせることができます。
List<T>代わりにSystem.ComponentModel.ObservableCollection<T>使用できます。 private ObservableCollection<User> _users;
public ObservableCollection<User> Users
{
get { return _users; }
set
{
if (_users == value) return;
_users = value;
NotifyPropertyChanged();
}
}
ObservableCollectionはCollectionChangedイベントを提供し、 INotifyPropertyChanged自体を実装します。 MSDNによると、イベントは "[..]アイテムが追加 、 削除 、 変更 、 移動 、またはリスト全体が更新さ れたときに上昇します"。
ただし、 ここで説明したように、コレクションの要素のプロパティが変更された場合、.NET 4.5.2以前ではObservableCollectionはCollectionChangedイベントを発生させません。
TrulyObservableCollection<T>のないINotifyPropertyChangedための制約T 、我々は必要なすべてを持つと天気をさらすT実装INotifyPropertyChangedかどうか: /*
* Original Class by Simon @StackOverflow http://stackoverflow.com/a/5256827/3766034
* Removal of the INPC-Constraint by Jirajha @StackOverflow
* according to to suggestion of nikeee @StackOverflow http://stackoverflow.com/a/10718451/3766034
*/
public sealed class TrulyObservableCollection<T> : ObservableCollection<T>
{
private readonly bool _inpcHookup;
public bool NotifyPropertyChangedHookup => _inpcHookup;
public TrulyObservableCollection()
{
CollectionChanged += TrulyObservableCollectionChanged;
_inpcHookup = typeof(INotifyPropertyChanged).GetTypeInfo().IsAssignableFrom(typeof(T));
}
public TrulyObservableCollection(IEnumerable<T> items) : this()
{
foreach (var item in items)
{
this.Add(item);
}
}
private void TrulyObservableCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (NotifyPropertyChangedHookup && e.NewItems != null && e.NewItems.Count > 0)
{
foreach (INotifyPropertyChanged item in e.NewItems)
{
item.PropertyChanged += ItemPropertyChanged;
}
}
if (NotifyPropertyChangedHookup && e.OldItems != null && e.OldItems.Count > 0)
{
foreach (INotifyPropertyChanged item in e.OldItems)
{
item.PropertyChanged -= ItemPropertyChanged;
}
}
}
private void ItemPropertyChanged(object sender, PropertyChangedEventArgs e)
{
var args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, sender, sender, IndexOf((T)sender));
OnCollectionChanged(args);
}
}
プロパティ・UsersをViewModelのTrulyObservableCollection<User>として定義します
private TrulyObservableCollection<string> _users;
public TrulyObservableCollection<string> Users
{
get { return _users; }
set
{
if (_users == value) return;
_users = value;
NotifyPropertyChanged();
}
}
私たちのUIは、Collection内の要素のINPC-Propertyが変更されるたびに通知され、すべての単一のBindingを再作成する必要はありません。