1. 程式人生 > 其它 >WPF_15_格式化繫結的資料

WPF_15_格式化繫結的資料

為了得到更人性化的外觀,需要設計如何修剪資料列表和資料欄位。

為了得到更人性化的外觀,需要設計如何修剪資料列表和資料欄位。

資料轉換

在基本繫結中,資訊從源到目標傳遞過程沒有任何變化。但有時候希望將資訊轉換到更友好的內容再呈現到介面上。WPF提供了兩個工具:

  • 字串格式化
  • 值轉換器

單個屬性

Binding.StringFormat 屬性針對簡單的,標準的格式化數字和日期而建立的。

<TextBox Text="{Binding Path=UnitCost, StringFormat={}{0:C}}"/>
<TextBox Text="{Binding Path=UnitCost, StringFormat=The value is {0:C}.}"/>
<ListBox DisplayMemberPath="UnitCost" ItemStringFormat="{0:C}"/>

值轉換器功能更強大,建立值轉換器需要4個步驟:

  1. 建立一個實現了 IValueConverter 介面的類
  2. 為該類宣告新增 ValueConversion 特性,並指定目標資料型別
  3. 實現 Convert() 方法
  4. 實現 ConvertBack() 方法
[ValueConversion(typeof(decimal), typeof(string))]
public class PriceConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        decimal price = (decimal)value;
        return price.ToString("C", culture);
    }
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        string price = value.ToString(cultere);
        decimal result;
        if(Decimal.TryParse(price, NumberStyles.Any, cultere, out result))
            return result;
        return value;
    }
}
<!--在Resources中建立轉換器物件,可以用於多個繫結-->
<Window.Resources>
    <local:PriceConverter x:Key="PriceConverter"/>
</Window.Resources>

<TextBox Text="{Binding Path=UnitCost, Converter={StaticResource PriceConverter}}"/>

多個屬性

<TextBlock>
    <TextBlock.Text>
        <!--使用 MultiBinding 替換 Binding-->
        <MultiBinding StringFromat="{1}, {0}">
            <Binding Path="FirstName"/>
            <Binding Path="LastName"/>
        </MultiBinding>
    </TextBlcok.Text>
</TextBlock>

如果希望完成更復雜的工作,需要使用值轉換器:

<TextBox>
    <TextBox.Text>
        <MultiBinding Converter="{StaticResource ValueInStockConverter}">
            <Binding Path="UnitCost"/>
            <Binding Path="UnitsInStock"/>
        </MultiBinding>
    </TextBox.Text>
</TextBox>
public class VallueInStockConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        decimal unitCost = (decimal)values[0];
        int unitsInStock = (int)value[1];
        return unitCost * unitsInStock;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}

列表控制元件

ItemsControl 類為封裝列表中的控制元件定義了基本功能,所有列表控制元件都繼承自該類。

屬性名 說明
ItemsSource 資料來源
DisplayMemberPath 期望資料項顯示的屬性 (更復雜的顯示使用ItemTemplate)
ItemStringFormat 為每個項格式化文字
ItemContainerStyle 通過樣式可以設定封裝每個項的容器的多個屬性。自動建立這些封裝器物件
ItemContainerStyleSelector 為每項的封裝器選擇樣式的StyleSelector物件
AIternationCount 在資料中設定的交替集合數量
ItemTemplate 模板從繫結的物件提取合適的資料並安排到合適的控制元件組合中
ItemTemplateSelector 為每個項選擇模板的 DataTemplateSelector 物件
ItemsPanel 用於包含列表中項的面板,所有封裝器都新增到這個容器中
GroupStyle 定義應當如何格式化每個分組
GroupStyleSelector 為每個分組選擇樣式的 StyleSelector 物件

列表樣式

ItemContainerStyle

當建立列表項時,列表控制元件會將其向下傳遞 ItemContainerStyle 屬性,每個列表項都將應用該樣式。

<ListBox Name="lstProducts" Margin="5" DisplayMemberPath="ModelName">
    <ListBox.ItemContainerStyle>
        <Style TargetType="{x:Type ListBoxItem}">
            <Setter Property="Background" Value="LightSteelBlue"/>
            <Setter Property="Margin" Value="5"/>
            <Setter Property="Padding" Value="5"/>
            <!--觸發器使得樣式更加精彩-->
            <style.Triggers>
                <Trigger Property="IsSelected" Value="True">
                    <Setter Property="Background" Value="DarkRed"/>
                    <Setter Property="Forground" Value="White"/>
                    <Setter Property="BorderBrush" Value="Blcak"/>
                    <Setter Property="BorderThickness" Value="1"/>
                </Trigger>
            </Style.Triggers>
        </Style>
    <ListBox.ItemContainerStyle>
</ListBox>

可以讓每個 ListBoxItem 物件在項文字的旁邊顯示單選按鈕或複選框

<Window.Resources>
    <Style x:Key="RadioButtonListStyle" TargetType="{x:Type ListBox}">
        <Setter Property="ItemContainerStyle">
            <Setter.Value>
                <Style TargetType="{x:Type ListBoxItem">
                    <Setter Property="Margin" Value="2"/>
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type ListBoxItem}">
                                <RadioButton Focusable="False" IsChecked="{Binding Path=IsSelected,Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}">
                                    <!--ContentPresenter獲取最初在項中顯示的內容-->
                                    <!--可能是文字,也可能是複雜的表示形式-->
                                    <ContentPresenter/>
                                </RadioButton>
                                <!-- 多選框
                                    <CheckBox Focusable="False" IsChecked="{Binding Path=IsSelected, Model=TwoWay,RelativeSource={RelativeSource TemplatedParent}}">
                                        <ContentPresenter/>
                                    </CheckBox>
                                -->
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </Setter.Value>
        </Setter>
    </Style>
</Window.Resources>
交替條目樣式

AlternationCount指定序列中項的數量,經過改數量後交替樣式。如果設定為2,第一個ListBoxItem的 AlternationIndex=0,第二個為1,第三個為0,第四個為1……。

<Style.Triggers>
    <Trigger Property="ItemsControl.AlternationIndex" Value="1">
        <Setter Property="Background" Value="LightBlue"/>
    </Trigger>
</Style.Triggers>

資料模板

樣式提供了基本的格式化能力,但不管如何修改ListBoxItem,它都只是ListBoxItem.資料模板是一塊定義如何顯示繫結的資料物件的XAML,有兩種型別的控制元件支援資料模板:

  • 內容控制元件通過 ContentTemplate 屬性支援資料模板
  • 列表控制元件通過 ItemTemplate 屬性支援資料模板

分離和重用模板

與樣式類似,通常也將模板宣告為視窗或程式的資源。

<Window.Resources>
    <DataTemplate x:Key="ProductDataTemplate">
        <Border Margin="5" BorderThickness="1" BorderBrush="StellBlue" CornerRadius="4"
            Background="{Binding Path=Background, RelativeSource={
                RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem}}}">
            <Grid Margin="3">
                <Grid.RowDefinitions>
                    <RowDefinition/>
                    <RowDefinition/>
                </Grid.RouDefinition>
                <TextBlock FontWeight="Bold" Text="{Binding Path=ModelNumber}"/>
                <TextBlock Grid.Row="1" Text="{Binding Path=ModelName}"/>
            </Grid>
        </Border>
    </DataTemplate>
</Window.Resources>

通過 StaticResource 引用來為列表新增資料模板:

<ListBox Name="lstProducts" HorizontalContentAlignment="Stretch"
    ItemTemplate="{StaticResource ProductDataTemplate}"/>

如果希望在不同型別的控制元件中自動重用相同的模板,可以通過設定 DataTemplate.DataType 屬性來確定使用模板的繫結資料的型別。

<Window.Resources>
    <!--模板將用於視窗中任何繫結到Product物件的列表控制元件或內容控制元件-->
    <DataTemplate DataType="{x:Type local:Product}">
    ...
    </DataTemplate>
</Window.Resources>

改變模板

目前只能為整個列表使用一個模板,如果希望採用不同方式靈活展示不同的資料:

  • 使用資料觸發器
  • 使用值轉換器
  • 使用模板選擇器

模板選擇器檢查繫結物件並使用提供的邏輯選擇合適的模板,需要建立繼承自 DataTemplateSelector 的類。

ComboBox控制元件

與ListBox類不同的是,ComboBox類增加了另外兩個部分:顯示當前選擇項的選擇框和用於選擇項的下拉列表。

ComboBox提供了自動完成輸入功能,當鍵入內容時,WPF使用第一個匹配自動完成建議的項填充選擇框中的剩餘內容。可以通過設定 ComboBox.IsTextSearchEnabled 屬性設定為 false 禁用該功能。

如果IsEditable屬性為 true,ComboBox控制元件不是顯示選擇項的副本,而是顯示選擇項的文字形式表示,WPF簡單呼叫ToString()方法。可以通過設定 TextSearch.TextPaht 附加屬性來定義選擇框顯示的內容:

<ComboBox IsEditable="True" IsReadOnly="True" TextSearch.TextPath="ModelName">
...
</ComboBox>

我的公眾號