65.9K
CodeProject 正在变化。 阅读更多。
Home

EF Code First 和 WPF 与 Chinook 数据库。第 3 部分 – 样式和 DataTemplates 101

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2投票s)

2015 年 1 月 12 日

CPOL

4分钟阅读

viewsIcon

8169

EF Code First 和 WPF 与 Chinook 数据库 - 样式和 DataTemplates

我决定将样式工作拆分成两篇文章,因为即使是重新设置如此简单的东西的样式也会是一篇巨大的文章。所以这篇文章将更改控件以更注重样式,并介绍基本样式,下一篇文章将介绍一些更高级的概念,例如使用修饰器来显示额外的详细信息。

回到上一篇文章,我们有一个基本的应用程序,如下所示

这有点难看,需要稍加修饰,所以我想添加的第一件事是一个可以整理艺术家列表的样式。

我希望艺术家列表看起来美观且具有圆角按钮类型的项目,并且还包含专辑数量的计数 - 我希望列表中的每个项目看起来像这样

image

实现此设计的第一个步骤是添加DataTemplate,以便项目拾取它。DataTemplate 是一个应用于没有默认方式显示自身并表现为其类型名称的对象的模板。

注意 – 这也可以通过重写 Object.ToString() 来完成,但这只是客户端的帖子,所以我不希望为了完成某些事情而更改模型代码。

我在 App.xamlApplication.Resources 部分添加了一个简单的 DataTemplate,如下所示

<DataTemplate DataType="{x:Type business:Artist}">
    <TextBlock x:Name="contentHolder">
        <TextBlock.Text>
            <MultiBinding StringFormat="{}{0} - {1} albums">
                <Binding Path="Name"/>
                <Binding Path="Albums.Count" />
            </MultiBinding>
        </TextBlock.Text>                
    </TextBlock>
    <DataTemplate.Triggers>
        <DataTrigger Binding="{Binding Albums.Count}" Value="1">
            <DataTrigger.Setters>
                <Setter Property="TextBlock.Text" TargetName="contentHolder">
                    <Setter.Value>
                        <MultiBinding StringFormat="{}{0} - {1} album">
                            <Binding Path="Name"/>
                            <Binding Path="Albums.Count" />
                        </MultiBinding>
                    </Setter.Value>
                </Setter>
            </DataTrigger.Setters>
        </DataTrigger>
    </DataTemplate.Triggers>
</DataTemplate>

这里发生了一些事情

  1. 第一行声明了此模板关联的类型 – 您必须首先将命名空间添加到此文件,才能使其生效
    xmlns:business="clr-namespace:MusicApp.Model;assembly=MusicApp.Model"
    
  2. MultiBinding 具有关联的 StringFormat – 这与普通的格式 string 相同,除了“{}”在等号之后 – 这只是转义格式 string
  3. 绑定到 Albums.Count – 是的,您也可以绑定到“内置”属性。
  4. DataTrigger 是一种简单的方式,如果只有一张专辑,那么尾随文本不应该在末尾加上“s”。

可以在转换器中完成整个 string,但我喜欢声明式方式,因为您可以在一个地方看到所有发生的事情。对于那些喜欢使用转换器的人,您可以将 TextBlockText 属性绑定到整个 Artist ({Binding}),然后使用如下转换器

public class ArtistStringConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, 
				System.Globalization.CultureInfo culture)
    {
        string retVal = string.Empty;
        var artist = value as Artist;
        if (artist != null)
        {
            retVal += string.Format("{0} - {1} {2}", artist.Name, 
			artist.Albums.Count, artist.Albums.Count == 1 ? "artist" : "artists");
        }

        return retVal;
    }

    public object ConvertBack(object value, Type targetType, object parameter, 
			System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

然后从 XAML 中使用,如下所示

<local:ArtistStringConverter x:Key="artistConverter"/>
<DataTemplate DataType="{x:Type business:Artist}">
    <TextBlock x:Name="contentHolder" Text="{Binding Converter={StaticResource artistConverter}}" />
</DataTemplate>

添加此项(任一选项)后,您需要从 ArtistList.xaml 视图中删除 DisplayMemberPath 属性,以便使用该模板。

我现在有一个 Artists 列表,显示他们的姓名和他们拥有的专辑总数,所以我可以处理实际样式,使列表项看起来像上面的图片。

我希望所有三个列表看起来都一样,所以我正在将一个没有键的样式添加到 app.xaml 文件中。如果您希望为您的控件添加特定样式,则可以添加“x:Key=”YourKeyName””并使用“{StaticResource YourKeyName}”引用它(在这种情况下,引用将在 ListBoxItemContainerStyle 属性中)。

<Style TargetType="ListBoxItem">
    <Style.Setters>
        <Setter Property="Margin" Value="5,2" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="ListBoxItem">
                    <Grid>
                        <Rectangle Opacity="0.5" Height="30" StrokeThickness="1" x:Name="backBox"
                                        Stroke="Silver" RadiusX="5" RadiusY="5" Fill="Azure"/>
                        <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="Selector.IsSelected" Value="True">
                            <Setter TargetName="backBox" Property="Fill" Value="Silver"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style.Setters>
</Style>

同样,我在此样式中也有一些事情正在发生

  1. 它以 ListBoxItem 为目标,因为这是我们想要设置样式的对象(而不是 ListBox 本身)。
  2. 边距很好地分隔了项目。
  3. 实际的样式部分包含在 ControlTemplate 中,然后将其分配给 ListBoxItemTemplate 属性。
  4. ContentPresenter 将呈现它给出的任何内容,在这种情况下,它正在呈现传入的 DataTemplate
  5. 此触发器指示当 ListBoxItem 被选中时,我们希望更改矩形的填充颜色,以指示我们已选择一个项目。

最后,我为专辑添加了一个新的 DataTemplate – 这看起来相同,类型和属性名称已更改为显示正确的内容,并且我也重新排列了视图,如下所示

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="0.5*" />
        <ColumnDefinition Width="0.5*" />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="0.5*"/>
        <RowDefinition Height="0.5*"/>
    </Grid.RowDefinitions>

    <ListBox ItemsSource="{Binding Path=Artists}" Margin="5" 
                Grid.Column="0" Grid.Row="0"
                Grid.RowSpan="2"
                SelectedItem="{Binding SelectedArtist}" />
        
    <ListBox ItemsSource="{Binding Path=SelectedArtist.Albums}" Margin="5" 
                Grid.Column="1" Grid.Row="0" 
                SelectedItem="{Binding SelectedAlbum}" />
        
    <ListBox ItemsSource="{Binding Path=SelectedAlbum.Tracks}" Margin="5" 
                DisplayMemberPath="Name" 
                Grid.Column="1" Grid.Row="1" />
</Grid>

这现在给了我以下视图,我相信您会同意看起来好多了,并且只需使用 XAML 就可以轻松实现 – 我们也可以在 blend 中完成此操作,但对于我们所做的简单更改,我更喜欢直接编辑 XAML。

image

在下一篇文章中,我将为屏幕添加一些修饰器,用于显示专辑和曲目详细信息(这也涵盖了对模型的更改)。


© . All rights reserved.