數(shù)據(jù)綁定
NoesisGUI提供了一種簡(jiǎn)單而強(qiáng)大的方法來(lái)自動(dòng)更新業(yè)務(wù)模型和用戶界面之間的數(shù)據(jù)。這種機(jī)制稱為數(shù)據(jù)綁定。它們是數(shù)據(jù)綁定的關(guān)鍵,是一個(gè)綁定對(duì)象,該對(duì)象將兩個(gè)屬性“粘合”在一起并保持它們之間的通信通道暢通。您可以設(shè)置一次綁定,然后讓它在應(yīng)用程序的剩余生命周期中完成所有同步工作。
在本教程中,我們將說(shuō)明您可能希望使用數(shù)據(jù)綁定的不同方式。
在XAML中使用綁定

要在XAML中使用綁定,可以直接將target屬性設(shè)置為Binding實(shí)例,然后使用標(biāo)準(zhǔn)標(biāo)記擴(kuò)展語(yǔ)法設(shè)置其屬性。下面的示例顯示了TextBox的文本和Label之間的簡(jiǎn)單綁定,該綁定反映了鍵入的值:
<StackPanel
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
VerticalAlignment="Center">
<TextBox x:Name="textbox" />
<Label Content="{Binding Text, ElementName=textbox}" />
</StackPanel>
注意
數(shù)據(jù)綁定的來(lái)源可以是常規(guī)屬性或DependencyProperty。綁定的目標(biāo)屬性必須是DependencyProperty。
數(shù)據(jù)語(yǔ)境
同一UI中的許多元素綁定同一源對(duì)象是很常見(jiàn)的。因此,noesisGUI支持指定隱式數(shù)據(jù)源,而不是使用 Source,RelativeSource或ElementName顯式標(biāo)記每個(gè)Binding。此隱式數(shù)據(jù)源也稱為數(shù)據(jù)上下文。
要將源對(duì)象指定為數(shù)據(jù)上下文,只需找到一個(gè)公共父元素并將其DataContext屬性設(shè)置為該源對(duì)象即可。在沒(méi)有顯式源對(duì)象的情況下遇到 Binding時(shí),noesisGUI會(huì)遍歷邏輯樹(shù),直到找到非null的DataContext為止。這是一個(gè)設(shè)置數(shù)據(jù)上下文的示例,該示例將用于以后的所有示例:
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<UserControl.Resources>
<DataModel x:Name="dataModel" />
</UserControl.Resources>
<StackPanel DataContext="{StaticResource dataModel}" />
</UserControl>
注意
在實(shí)際項(xiàng)目中,更常見(jiàn)的是通過(guò)代碼設(shè)置數(shù)據(jù)上下文
綁定到普通屬性
NoesisGUI支持將任何對(duì)象的任何常規(guī)屬性用作數(shù)據(jù)綁定源。例如,以下XAML綁定了幾個(gè)屬性,這些屬性屬于在當(dāng)前數(shù)據(jù)上下文中找到的實(shí)例:
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<UserControl.Resources>
<DataModel1 x:Name="dataModel">
<DataModel1.Person>
<Person Weight="90">
<Person.Name>
<Name First="John" Last="Doe" />
</Person.Name>
</Person>
</DataModel1.Person>
</DataModel1>
</UserControl.Resources>
<StackPanel DataContext="{StaticResource dataModel}" HorizontalAlignment="Center"
VerticalAlignment="Center">
<TextBlock Text="{Binding Person.Name.First}" />
<TextBlock Text="{Binding Person.Name.Last}" />
<TextBlock Text="{Binding Person.Weight}" />
</StackPanel>
</UserControl>
但是,使用平原屬性作為數(shù)據(jù)綁定源存在很大的警告。由于此類屬性沒(méi)有自動(dòng)檢測(cè)更改通知的對(duì)象,因此在不做任何額外工作的情況下,由于源屬性值發(fā)生更改,因此目標(biāo)無(wú)法保持最新??。為了使目標(biāo)屬性和源屬性保持同步,源對(duì)象必須實(shí)現(xiàn)INotifyPropertyChanged接口。
C ++實(shí)現(xiàn)
每當(dāng)在C ++中實(shí)現(xiàn)數(shù)據(jù)模型時(shí),都必須注意一些重要的細(xì)節(jié):
- 基類NotifyPropertyChangedBase已經(jīng)為我們實(shí)現(xiàn)了INotifyPropertyChanged接口
- 使用帶有g(shù)etter和setter函數(shù)的反射宏宏公開(kāi)屬性
- 在每個(gè)設(shè)置器中,檢查是否必須觸發(fā)屬性更改事件
C ++
class Name: public NotifyPropertyChangedBase
{
public:
const char* GetFirst() const
{
return _first.c_str();
}
void SetFirst(const char* first)
{
if (_first != first)
{
_first = first;
OnPropertyChanged("First");
}
}
const char* GetLast() const
{
return _last.c_str();
}
void SetLast(const char* last)
{
if (_last != last)
{
_last = last;
OnPropertyChanged("Last");
}
}
private:
NsString _first;
NsString _last;
NS_IMPLEMENT_INLINE_REFLECTION(Name, NotifyPropertyChangedBase)
{
NsMeta<TypeId>("Name");
NsProp("First", &Name::GetFirst, &Name::SetFirst);
NsProp("Last", &Name::GetLast, &Name::SetLast);
}
};
class Person: public NotifyPropertyChangedBase
{
public:
Name* GetName() const
{
return _name.GetPtr();
}
void SetName(Name* name)
{
if (_name != name)
{
_name.Reset(name);
OnPropertyChanged("Name");
}
}
float GetWeight() const
{
return _weight;
}
void SetWeight(const float weight)
{
if (_weight != weight)
{
_weight = weight;
OnPropertyChanged("Weight");
}
}
private:
Ptr<Name> _name;
float _weight;
NS_IMPLEMENT_INLINE_REFLECTION(Person, BaseComponent)
{
NsMeta<TypeId>("Person");
NsProp("Name", &Person::GetName, &Person::SetName);
NsProp("Weight", &Person::GetWeight, &Person::SetWeight);
}
};
class DataModel1: public NotifyPropertyChangedBase
{
public:
Person* GetPerson() const
{
return _person.GetPtr();
}
void SetPerson(Person* person)
{
if (_person != person)
{
_person.Reset(person);
OnPropertyChanged("Person");
}
}
private:
Ptr<Person> _person;
NS_IMPLEMENT_INLINE_REFLECTION(DataModel1, BaseComponent)
{
NsMeta<TypeId>("DataModel1");
NsProp("Person", &DataModel1::GetPerson, &DataModel1::SetPerson);
}
};
C#實(shí)現(xiàn)
在C#中,事情要容易得多。您基本上只需要實(shí)現(xiàn)INotifyPropertyChanged接口。
C#
public class Name: INotifyPropertyChanged
{
private string _first;
public string First
{
get { return _first; }
set
{
if (_first != value)
{
_first = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("First"));
}
}
}
}
private string _last;
public string Last
{
get { return _last; }
set
{
if (_last != value)
{
_last = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Last"));
}
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
public class Person: INotifyPropertyChanged
{
private Name _name;
public Name Name
{
get { return _name; }
set
{
if (_name != value)
{
_name = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Name"));
}
}
}
}
private float _weight;
public float Weight
{
get { return _weight; }
set
{
if (_weight != value)
{
_weight = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Weight"));
}
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
public class DataModel1
{
private Person _person;
public Person Person
{
get { return _person; }
set
{
if (_person != value)
{
_person = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Person"));
}
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
索引屬性
在C#中,索引屬性可以用作數(shù)據(jù)綁定的源。這樣,您可以編寫(xiě)如下內(nèi)容:
C#
class Person
{
public string Name {get; set;}
public string PhoneNumber {get; set;}
}
class Contacts
{
public IEnumerable<Person> Persons {get; set;}
public Person this[string Name]
{
get
{
return Persons.Where(p => p.Name == Name).FirstOrDefault();
}
}
}
<TextBox Text="{Binding Contacts[John].PhoneNumber}" />
注意
noesisGUI僅支持鍵類型為int或string的索引器。
字符串格式
如果需要格式化給定值的文本顯示,則可以使用綁定聲明中的StringFormat屬性來(lái)格式化。使用StringFormat,您可以格式化信息輸出的格式,而無(wú)需使用背后的代碼或值轉(zhuǎn)換器。
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<UserControl.Resources>
<DataModel1 x:Name="dataModel">
<DataModel1.Person>
<Person Weight="90">
<Person.Name>
<Name First="John" Last="Doe" />
</Person.Name>
</Person>
</DataModel1.Person>
</DataModel1>
</UserControl.Resources>
<StackPanel DataContext="{StaticResource dataModel}" HorizontalAlignment="Center"
VerticalAlignment="Center">
<TextBlock Text="{Binding Person.Name.First}" />
<TextBlock Text="{Binding Person.Name.Last}" />
<TextBlock Text="{Binding Person.Weight, StringFormat=Weight is {0:F2}}" />
</StackPanel>
</UserControl>
StringFormat的更多示例:
<TextBlock Text="{Binding Amount, StringFormat={}{0:D}}" />
<TextBlock Text="{Binding Amount, StringFormat=Total: {0:D}}" />
<TextBlock Text="{Binding Amount, StringFormat=Total: {0:D} units}" />
注意
注意StringFormat屬性后面的“ {}”嗎?這樣做是在'='符號(hào)后轉(zhuǎn)義文本。這是必需的,因?yàn)?='符號(hào)后沒(méi)有文本
支持標(biāo)準(zhǔn),自定義和復(fù)合 .NET數(shù)字格式字符串。
標(biāo)準(zhǔn)數(shù)字格式字符串
| 格式說(shuō)明符 | 名稱 | 描述 | 例 |
|---|---|---|---|
| “ C”或“ c” | 貨幣 | 貨幣值 | 123.456(“ C”)?$ 123,46 |
| “ D”或“ d” | 小數(shù) | 帶可選負(fù)號(hào)的整數(shù) | 1234(“ D”)?1234 |
| “ E”或“ e” | 指數(shù)的 | 指數(shù)通知 | 1052.0329112756(“ E”)?1.052033E + 003 |
| “ F”或“ f” | 固定點(diǎn) | 帶有負(fù)號(hào)的整數(shù)和十進(jìn)制數(shù)字 | 234.567(“ F”)?1234.57 |
| “ G”或“ g” | 一般 | 定點(diǎn)數(shù)或科學(xué)記數(shù)法的緊湊性 | -123.456(“ G”)?-123.456 |
| “ N”或“ n” | 數(shù) | 整數(shù)和十進(jìn)制數(shù)字,組分隔符以及帶可選負(fù)號(hào)的十進(jìn)制分隔符 | 1234.567(“ N”)?1,234.57 |
| “ P”或“ p” | 百分 | 數(shù)字乘以100并顯示百分號(hào) | 1(“ P”)?100.00% |
| “ R”或“ r” | 往返 | 可以往返到相同數(shù)字的字符串 | 123456789.12345678(“ R”)?123456789.12345678 |
| “ X”或“ x” | 十六進(jìn)制 | 十六進(jìn)制字符串 | 255(“ X”)?FF |
自定義數(shù)字格式字符串
| 格式說(shuō)明符 | 名稱 | 描述 | 例 |
|---|---|---|---|
| “ 0” | 零占位符 | 如果存在一個(gè)數(shù)字,則將零替換為相應(yīng)的數(shù)字;否則,結(jié)果字符串中將出現(xiàn)零 | 1234.5678(“ 00000”)?01235 |
| “?!?/td> | 數(shù)字占位符 | 如果存在數(shù)字,則用相應(yīng)的數(shù)字替換“#”符號(hào);否則,結(jié)果字符串中不會(huì)出現(xiàn)數(shù)字 | 1234.5678(“ #####”)?1235 |
| “。” | 小數(shù)點(diǎn) | 確定小數(shù)點(diǎn)分隔符在結(jié)果字符串中的位置 | 0.45678(“ 0.00”)?0.46 |
| “,” | 組分隔符和數(shù)字縮放 | 作為組分隔符和數(shù)字縮放說(shuō)明符。作為組分隔符,它在每個(gè)組之間插入一個(gè)本地化的組分隔符。作為數(shù)字縮放說(shuō)明符,它為指定的每個(gè)逗號(hào)將數(shù)字除以1000 |
2147483647(“ ##,?!保?2,147,483,647
2147483647(“#,#,”)?2,147
|
| “%” | 占位符百分比 | 將數(shù)字乘以100,然后在結(jié)果字符串中插入本地化的百分比符號(hào) | 0.3697(“%#0.00”)?%36.97 |
| “ E0” | 指數(shù)符號(hào) | 如果后跟至少一個(gè)0(零),則使用指數(shù)表示法格式化結(jié)果?!?E”或“ e”的大小寫(xiě)指示結(jié)果字符串中指數(shù)符號(hào)的大小寫(xiě)。“ E”或“ e”字符后的零位數(shù)確定指數(shù)中的最小位數(shù)。加號(hào)(+)表示符號(hào)始終位于指數(shù)之前。減號(hào)(-)表示符號(hào)字符僅在負(fù)指數(shù)之前 |
987654(“#0.0e0”)?98.8e4
1.8901385E-16(“ 0.0e + 00”)?1.9e-16
1503.92311(“ 0.0 ## e + 00”)?1.504e + 03
|
| “ \” | 轉(zhuǎn)義符 | 使下一個(gè)字符被解釋為文字而不是自定義格式說(shuō)明符 | 87654(“ \ ### 00 \?!保?#987654# |
| '串' | 文字字符串定界符 | 表示應(yīng)將包含的字符原樣復(fù)制到結(jié)果字符串中 | 68(“#'degrees'”)?68度 |
| ; | 節(jié)分隔符 | 為正,負(fù)和零數(shù)字定義帶有單獨(dú)格式字符串的部分 | 12.345(“#0.0#;(#0.0#);-0-”)?12.35 |
| 其他 | 所有其他字符 | 字符不變地復(fù)制到結(jié)果字符串 | 68(“?!恪保?68° |
綁定依賴項(xiàng)屬性
依賴性屬性具有內(nèi)置的更改通知管道。此功能是noesisGUI保持目標(biāo)屬性和源屬性同步的能力的關(guān)鍵。實(shí)現(xiàn)依賴項(xiàng)屬性時(shí),無(wú)需使用InotifyPropertyChange。實(shí)際上,如果您是從DependencyObject派生的,那么這應(yīng)該是創(chuàng)建可綁定屬性的首選方法。
依賴項(xiàng)屬性是在可視元素后面的代碼中強(qiáng)制創(chuàng)建的,例如:
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="View2"
x:Name="root">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBox Text="{Binding ElementName=root, Path=Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Text="{Binding ElementName=root, Path=Text}" Width="100" />
</StackPanel>
</UserControl>
如您所見(jiàn),我們正在設(shè)置Binding的Mode屬性??梢詫⑵湓O(shè)置為BindingMode枚舉的以下值之一:

- OneWay:只要源發(fā)生更改,目標(biāo)就會(huì)更新。
- TwoWay:對(duì)目標(biāo)或源的更改會(huì)更新另一個(gè)。
- OneWayToSource:所述的相對(duì)單向。每當(dāng)目標(biāo)更改時(shí),源就會(huì)更新。
- OneTime:它與OneWay一樣工作,除了對(duì)源所做的更改不會(huì)反映在目標(biāo)上。在綁定啟動(dòng)時(shí),目標(biāo)保留源的快照。
TwoWay綁定適用于數(shù)據(jù)綁定表單,在該表單中,您的TextBox可能填充了允許用戶更改的數(shù)據(jù)。實(shí)際上,盡管大多數(shù)依賴項(xiàng)屬性默認(rèn)為OneWay綁定,但是諸如TextBox.Text之類的依賴項(xiàng)屬性默認(rèn)為TwoWay綁定。
我們還使用UpdateSourceTrigger屬性。使用TwoWay或OneWayToSource綁定時(shí),您可能希望在何時(shí)以及如何更新源時(shí)使用不同的行為。例如,如果用戶鍵入TwoWay數(shù)據(jù)綁定的TextBox,您是否希望每次擊鍵都更新源,還是僅在用戶完成輸入后才更新源?通過(guò)綁定,您可以使用其UpdateSourceTrigger屬性來(lái)控制此類行為。
可以將UpdateSourceTrigger設(shè)置為UpdateSourceTrigger枚舉的成員,該枚舉具有以下值:
- PropertyChanged:每當(dāng)目標(biāo)屬性值更改時(shí),源就會(huì)更新。
- LostFocus:當(dāng)目標(biāo)屬性值更改時(shí),僅在目標(biāo)元素失去焦點(diǎn)之后才更新源。
- 顯式:僅當(dāng)您顯式調(diào)用BindingExpression.UpdateSource時(shí),才更新源。
正如不同的屬性具有不同的默認(rèn)模式設(shè)置一樣,它們也具有不同的默認(rèn)UpdateSourceTrigger設(shè)置。TextBox.Text默認(rèn)為LostFocus。
C ++
class View2: public UserControl
{
public:
const char* GetText() const
{
return GetValue<NsString>(TextProperty).c_str();
}
void SetText(const char* text)
{
SetValue<NsString>(TextProperty, text);
}
static const DependencyProperty* TextProperty;
private:
NS_IMPLEMENT_INLINE_REFLECTION(View2, UserControl)
{
NsMeta<TypeId>("View2");
Ptr<UIElementData> data = NsMeta<UIElementData>(TypeOf<SelfClass>());
data->RegisterProperty<NsString>(TextProperty, "Text",
FrameworkPropertyMetadata::Create(NsString(""), FrameworkOptions_None));
}
};
C#
class View2: UserControl
{
public static DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string),
typeof(View2), new PropertyMetadata(""));
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
}
綁定到收藏集
到目前為止,我們僅討論了綁定到單個(gè)對(duì)象的情況,但是,綁定到數(shù)據(jù)集合是一種常見(jiàn)的情況。例如,一種常見(jiàn)的情況是使用諸如ListBox,ListView或TreeView之類的ItemsControl來(lái)顯示數(shù)據(jù)集合。
創(chuàng)建具有ListBox.Items作為目標(biāo)屬性的Binding是有意義的,但是,可惜,Items不是依賴項(xiàng)屬性。但是ListBox(以及所有其他ItemsControl)具有ItemsSource依賴項(xiàng)屬性,該屬性專門(mén)用于此數(shù)據(jù)綁定方案。

<ListBox ItemsSource="{Binding Source={StaticResource items}}" />
為了使目標(biāo)屬性保持對(duì)源集合的更改進(jìn)行更新,源集合必須實(shí)現(xiàn)一個(gè)稱為INotifyCollectionChanged的接口。幸運(yùn)的是,noesisGUI已經(jīng)有一個(gè)內(nèi)置類可以為您完成此工作。它稱為ObservableCollection。
<Grid
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="600">
<Grid.Resources>
<DataModel3 x:Name="dataModel" />
<DataTemplate x:Key="TaskTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="200"/>
</Grid.ColumnDefinitions>
<Rectangle Width="15" Height="10" Fill="{Binding Color}" Stroke="Transparent"
StrokeThickness="0" Margin="5, 0, 5, 0" Grid.Column="0"/>
<TextBlock Text="{Binding Name}" Margin="5, 0, 5, 0" Grid.Column="1"/>
<TextBlock Text="{Binding Scale, StringFormat=P0}" Margin="5, 0, 5, 0" Grid.Column="2"/>
<TextBlock Text="{Binding Pos}" Margin="5, 0, 5, 0" Grid.Column="3"/>
</Grid>
</DataTemplate>
</Grid.Resources>
<ListBox Height="100" DataContext="{StaticResource dataModel}"
ItemsSource="{Binding Players}"
ItemTemplate="{StaticResource TaskTemplate}" />
</Grid>
請(qǐng)注意,我們使用ItemTemplate屬性來(lái)控制每個(gè)項(xiàng)目的呈現(xiàn)方式。此屬性設(shè)置為DataTemplate的實(shí)例。DataTemplate從FrameworkTemplate派生。因此,它具有VisualTree內(nèi)容屬性,可以將其設(shè)置為FrameworkElements的任意樹(shù)。
注意
對(duì)于非常簡(jiǎn)單的情況,可以使用所有ItemsControls上的DisplayMemberPath屬性。此屬性與ItemsSource協(xié)同工作。如果將其設(shè)置為適當(dāng)?shù)膶傩月窂?,則將為每個(gè)項(xiàng)目呈現(xiàn)相應(yīng)的屬性值。
應(yīng)用數(shù)據(jù)模板時(shí),會(huì)隱式地為其提供適當(dāng)?shù)臄?shù)據(jù)上下文。當(dāng)用作ItemTemplate時(shí),數(shù)據(jù)上下文隱式為ItemsSource中的當(dāng)前項(xiàng)目。

C ++
class Player: public BaseComponent
{
public:
Player() {}
Player(NsString name, Color color, float scale, NsString pos) : _name(name), _scale(scale),
_pos(pos), _color(*new SolidColorBrush(color)) {}
private:
NsString _name;
float _scale;
NsString _pos;
Ptr<Brush> _color;
NS_IMPLEMENT_INLINE_REFLECTION(Player, BaseComponent)
{
NsMeta<TypeId>("Player");
NsProp("Name", &Player::_name);
NsProp("Scale", &Player::_scale);
NsProp("Pos", &Player::_pos);
NsProp("Color", &Player::_color);
}
};
class DataModel3: public BaseComponent
{
public:
DataModel3()
{
_players = *new ObservableCollection<Player>;
Ptr<Player> player0 = *new Player("Player0", Color::Red, 1.0f, "(0,0,0)");
_players->Add(player0);
Ptr<Player> player1 = *new Player("Player1", Color::Gray, 0.75f, "(0,30,0)");
_players->Add(player1);
Ptr<Player> player2 = *new Player("Player2", Color::Orange, 0.50f, "(0,-10,0)");
_players->Add(player2);
Ptr<Player> player3 = *new Player("Player3", Color::Green, 0.85f, "(0,-10,0)");
_players->Add(player3);
}
private:
Ptr<ObservableCollection<Player>> _players;
NS_IMPLEMENT_INLINE_REFLECTION(DataModel3, BaseComponent)
{
NsMeta<TypeId>("DataModel3");
NsProp("Players", &DataModel3::_players);
}
};
C#
public class Player
{
public Player(string name, Color color, float scale, string pos)
{
Name = name;
Color = new SolidColorBrush(color);
Scale = scale;
Pos = pos;
}
public string Name { get; private set; }
public Brush Color { get; private set; }
public float Scale { get; private set; }
public string Pos { get; private set; }
}
public class DataModel3
{
public DataModel3()
{
Players = new ObservableCollection<Player>();
Players.Add(new Player("Player0", Colors.Red, 1.0f, "(0,0,0)"));
Players.Add(new Player("Player1", Colors.Gray, 0.75f, "(0,30,0)"));
Players.Add(new Player("Player2", Colors.Orange, 0.50f, "(0,-10,0)"));
Players.Add(new Player("Player3", Colors.Green, 0.85f, "(0,-10,0)"));
}
public ObservableCollection<Player> Players { get; private set; }
}
存在一個(gè)特殊的DataTemplate子類,用于處理分層數(shù)據(jù)。此類稱為HierarchicalDataTemplate。它不僅使您能夠更改此類數(shù)據(jù)的表示形式,而且還使您可以將對(duì)象的層次結(jié)構(gòu)直接綁定到本質(zhì)上理解層次結(jié)構(gòu)的元素,例如TreeView或Menu控件。
想法是對(duì)層次結(jié)構(gòu)中的每種數(shù)據(jù)類型使用HierarchicalDataTemplate,然后對(duì)任何葉節(jié)點(diǎn)使用簡(jiǎn)單的DataTemplate。每個(gè)數(shù)據(jù)模板都允許您自定義數(shù)據(jù)類型的呈現(xiàn)方式,但是HierarchicalDataTemplate還使您可以通過(guò)設(shè)置其ItemsSource屬性來(lái)在層次結(jié)構(gòu)中指定其子級(jí)。

<Grid
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid.Resources>
<LeagueList x:Key="items" />
<HierarchicalDataTemplate DataType="League" ItemsSource="{Binding Path=Divisions}">
<TextBlock Foreground="LightCyan" Text="{Binding Path=Name}"/>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="Division" ItemsSource="{Binding Path=Teams}">
<TextBlock Foreground="Snow" Text="{Binding Path=Name}"/>
</HierarchicalDataTemplate>
<DataTemplate DataType="Team">
<TextBlock Foreground="Moccasin" Text="{Binding Path=Name}"/>
</DataTemplate>
</Grid.Resources>
<TreeView DataContext="{StaticResource items}">
<TreeViewItem ItemsSource="{Binding Leagues}" Header="My Soccer Leagues" />
</TreeView>
</Grid>
價(jià)值轉(zhuǎn)換器
數(shù)據(jù)模板可以通過(guò)呈現(xiàn)某些目標(biāo)值的方式進(jìn)行自定義,而值轉(zhuǎn)換器可以將源值轉(zhuǎn)換為完全不同的目標(biāo)值。它們使您可以插入自定義邏輯,而不會(huì)放棄數(shù)據(jù)綁定的好處。
值轉(zhuǎn)換器通常用于協(xié)調(diào)不同數(shù)據(jù)類型的源和目標(biāo)。例如,您可以在某些非Brush數(shù)據(jù)源的值上更改元素的背景或前景色。或者,您可以使用它來(lái)簡(jiǎn)單地增強(qiáng)顯示的信息,而無(wú)需單獨(dú)的元素,例如在原始計(jì)數(shù)中添加“ item”后綴。

例如,以下XAML 使用名為BooleanToVisibilityConverter的轉(zhuǎn)換器,根據(jù)CheckBox的IsChecked屬性切換元素的可見(jiàn)性。
<Grid
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Background="Gray">
<Grid.Resources>
<BooleanToVisibilityConverter x:Key="converter"/>
</Grid.Resources>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<CheckBox x:Name="checkBox" Content="Show Button" Width="100"/>
<Button Content="Click" Margin="5" Visibility="{Binding ElementName=checkBox, Path=IsChecked,
Converter={StaticResource converter}}"/>
</StackPanel>
</Grid>
您可以通過(guò)繼承BaseValueConverter來(lái)創(chuàng)建自己的轉(zhuǎn)換器。擴(kuò)展教程中顯示了一個(gè)示例。
太陽(yáng)系實(shí)例

本示例總結(jié)了本教程中介紹的所有概念。它基本上由一個(gè)綁定到SolarSystemObject項(xiàng)目的ObservableCollection的ListBox組成。
<ListBox ItemsSource="{Binding Source={StaticResource solarSystem}, Path=SolarSystemObjects}" />
每個(gè)行星的外觀由DataTemplate定義,該DataTemplate使用數(shù)據(jù)綁定和轉(zhuǎn)換器生成最終外觀。
注意
此示例改編自WPF中樣式和模板的功能