教程13:數(shù)據(jù)綁定

數(shù)據(jù)綁定

教程數(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中使用綁定

DataBindingTutorialImg1.jpg

要在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ù)源,而不是使用 SourceRelativeSourceElementName顯式標(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è)置BindingMode屬性??梢詫⑵湓O(shè)置為BindingMode枚舉的以下值之一:

DataBindingTutorialImg2.jpg
  • 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屬性。使用TwoWayOneWayToSource綁定時(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,ListViewTreeView之類的ItemsControl來(lái)顯示數(shù)據(jù)集合。

創(chuàng)建具有ListBox.Items作為目標(biāo)屬性的Binding是有意義的,但是,可惜,Items不是依賴項(xiàng)屬性。但是ListBox(以及所有其他ItemsControl)具有ItemsSource依賴項(xiàng)屬性,該屬性專門(mén)用于此數(shù)據(jù)綁定方案。

image.png
<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í)例。DataTemplateFrameworkTemplate派生。因此,它具有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)目。

DataBindingTutorialImg6.jpg

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)的元素,例如TreeViewMenu控件。

想法是對(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í)。

DataBindingTutorialImg7.jpg
<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”后綴。

DataBindingTutorialImg4.jpg

例如,以下XAML 使用名為BooleanToVisibilityConverter的轉(zhuǎn)換器,根據(jù)CheckBoxIsChecked屬性切換元素的可見(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í)例

DataBindingTutorialImg5.jpg

示例總結(jié)了本教程中介紹的所有概念。它基本上由一個(gè)綁定到SolarSystemObject項(xiàng)目的ObservableCollectionListBox組成。

<ListBox ItemsSource="{Binding Source={StaticResource solarSystem}, Path=SolarSystemObjects}" />

每個(gè)行星的外觀由DataTemplate定義,該DataTemplate使用數(shù)據(jù)綁定和轉(zhuǎn)換器生成最終外觀。

注意

此示例改編自WPF中樣式和模板的功能

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容