WPF - 如何在与字符串列表绑定的 TextBlock 中正确显示文本
WPF - How to correctly show text in a TextBlock binded with a string list
为了以后的专业项目,我需要评估一下WPF的能力。
在这种情况下,我创建了一个小测试项目,其中包含 1 个字符串树和 1 个图像网格。我希望我的图像网格显示给定目录中包含的所有 jpeg 图像,并且对于每个图像,在图像下方显示提取的文件名,不包含其路径和扩展名。
实际上,我的演示根据我的目标正确运行,除了一点:我添加了每个格式化文件名以显示在列表集合中,我试图将其与每个图像底部显示的 TextBlock 绑定。然而,这个格式化的名称是不可见的,而是我看到了完整的文件名,就好像 TextBlock 直接从 Image 对象中提取它一样。
我尝试自己解决这个问题,遵循了几个教程,但没有任何效果。我无法弄清楚我做错了什么。有人可以给我解释一下吗?
这是我的xaml文件内容
<Window x:Class="VirtualTrees.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:VirtualTrees"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<Style x:Key="myHeaderStyle" TargetType="{x:Type GridViewColumnHeader}">
<Setter Property="Visibility" Value="Collapsed" />
</Style>
<DataTemplate x:Key="itImageCell">
<WrapPanel>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="100"/>
<RowDefinition Height="20"/>
</Grid.RowDefinitions>
<Image Width="120" Stretch="Uniform" Source="{Binding}"/>
<TextBlock Grid.Row="1" Width="120" Text="{Binding}" TextTrimming="CharacterEllipsis"/>
</Grid>
</WrapPanel>
</DataTemplate>
<local:ListToStringConverter x:Key="ListToStringConverter" />
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="400"/>
<ColumnDefinition Width="400*"/>
</Grid.ColumnDefinitions>
<ListView Margin="10" Name="lvStringTree">
<ListView.View>
<GridView>
<GridViewColumn Header="Name" Width="120" DisplayMemberBinding="{Binding Name}" />
<GridViewColumn Header="Age" Width="50" DisplayMemberBinding="{Binding Age}" />
<GridViewColumn Header="Mail" Width="150" DisplayMemberBinding="{Binding Mail}" />
</GridView>
</ListView.View>
</ListView>
<Grid x:Name="grImages" Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<ListView Grid.Row="1" Name="lvImages" ItemsSource="{Binding Path=m_ImageList}" ItemTemplate="{StaticResource itImageCell}">
<ListView.Background>
<ImageBrush/>
</ListView.Background>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="3" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
<TextBlock Name="tbImageName" Text="{Binding Path=m_ImageNames, Converter={StaticResource ResourceKey=ListToStringConverter}}" DataContext="{StaticResource itImageCell}" />
</Grid>
</Grid>
</Window>
还有我的 C# 代码
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace VirtualTrees
{
[ValueConversion(typeof(List<string>), typeof(string))]
public class ListToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (targetType != typeof(string))
throw new InvalidOperationException("The target must be a string");
return string.Join(", ", ((List<string>)value).ToArray());
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public class User
{
public string Name { get; set; }
public int Age { get; set; }
public string Mail { get; set; }
}
List<ImageSource> m_ImageList = new List<ImageSource>();
List<string> m_ImageNames = new List<string>();
string m_RegexPattern = @"\([\w ]+).(?:jpg|png)$";
public MainWindow()
{
InitializeComponent();
PopulateStringTree();
PopulateImageGrid();
}
public void PopulateStringTree()
{
List<User> vstItems = new List<User>();
for (ulong i = 0; i < 100000; ++i)
{
vstItems.Add(new User() { Name = "John Doe", Age = 42, Mail = "john@doe-family.com" });
vstItems.Add(new User() { Name = "Jane Doe", Age = 39, Mail = "jane@doe-family.com" });
vstItems.Add(new User() { Name = "Sammy Doe", Age = 7, Mail = "sammy.doe@gmail.com" });
}
lvStringTree.ItemsSource = vstItems;
}
public void PopulateImageGrid()
{
// get jpeg image file list from target dir
string moviePosterPath = @"W:\Labo\WPF\VirtualTrees\VirtualTrees\Resources\Images";
List<string> fileNames = new List<string>(System.IO.Directory.EnumerateFiles(moviePosterPath, "*.jpg"));
// iterate through files
foreach (string fileName in fileNames)
{
// load image and add it to image list
m_ImageList.Add(new BitmapImage(new Uri(fileName)));
Console.WriteLine("filename " + fileName);
// extract image file name and add it to name list
Match regexMatch = Regex.Match(fileName.Trim(), m_RegexPattern);
m_ImageNames.Add(regexMatch.Groups[1].Value);
Console.WriteLine("Movie Name: " + regexMatch.Groups[1].Value);
}
// bind data to image grid
lvImages.ItemsSource = m_ImageList;
}
}
}
你的DataTemplate
是错误的来源。您必须检查 TextBlock
的绑定。您正在绑定到 DataContext
,这是一个 BitmapSource
。 TextBlock
隐式调用 BitmapSource.ToString()
以获取该类型的字符串表示形式。 BitmapSource
已将 ToString()
覆盖为 return 完整文件路径。要解决此问题,您需要使用 IValueConverter
.
已修改 DataTemplate
。 TextBlock
绑定现在使用转换器将 BitmapSource
转换为文件名:
<DataTemplate x:Key="itImageCell">
<WrapPanel>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="100" />
<RowDefinition Height="20" />
</Grid.RowDefinitions>
<Image Width="120"
Stretch="Uniform"
Source="{Binding}" />
<TextBlock Grid.Row="1"
Width="120"
Text="{Binding ., Converter={StaticResource BitmapSourceToFilenameConverter}}"
TextTrimming="CharacterEllipsis" />
</Grid>
</WrapPanel>
</DataTemplate>
TextBlock
绑定的 IValueConverter 将 BitmapSource
转换为文件名:
[ValueConversion(typeof(BitmapSource), typeof(string))]
public class BitmapSourceToFilenameConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is BitmapSource bitmapSource)
return bitmapSource.UriSource.AbsolutePath;
return Binding.DoNothing;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
我在你的代码中发现了一个小错误:
您首先在 ListView
:
上设置绑定
<ListView Name="lvImages" ItemsSource="{Binding Path=m_ImageList}" />
然后您覆盖(删除)它
// bind data to image grid
lvImages.ItemsSource = m_ImageList;
这不是绑定(评论不正确)。
您应该将 m_ImageList
设为 ObservableCollection<ImageSource>
而不是 List
。添加、移动或删除项目时,ObservableCollection
将自动更新 ListView
。然后从您的 MainWindow
class 中删除此行:lvImages.ItemsSource = m_ImageList;
为了以后的专业项目,我需要评估一下WPF的能力。
在这种情况下,我创建了一个小测试项目,其中包含 1 个字符串树和 1 个图像网格。我希望我的图像网格显示给定目录中包含的所有 jpeg 图像,并且对于每个图像,在图像下方显示提取的文件名,不包含其路径和扩展名。
实际上,我的演示根据我的目标正确运行,除了一点:我添加了每个格式化文件名以显示在列表集合中,我试图将其与每个图像底部显示的 TextBlock 绑定。然而,这个格式化的名称是不可见的,而是我看到了完整的文件名,就好像 TextBlock 直接从 Image 对象中提取它一样。
我尝试自己解决这个问题,遵循了几个教程,但没有任何效果。我无法弄清楚我做错了什么。有人可以给我解释一下吗?
这是我的xaml文件内容
<Window x:Class="VirtualTrees.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:VirtualTrees"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<Style x:Key="myHeaderStyle" TargetType="{x:Type GridViewColumnHeader}">
<Setter Property="Visibility" Value="Collapsed" />
</Style>
<DataTemplate x:Key="itImageCell">
<WrapPanel>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="100"/>
<RowDefinition Height="20"/>
</Grid.RowDefinitions>
<Image Width="120" Stretch="Uniform" Source="{Binding}"/>
<TextBlock Grid.Row="1" Width="120" Text="{Binding}" TextTrimming="CharacterEllipsis"/>
</Grid>
</WrapPanel>
</DataTemplate>
<local:ListToStringConverter x:Key="ListToStringConverter" />
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="400"/>
<ColumnDefinition Width="400*"/>
</Grid.ColumnDefinitions>
<ListView Margin="10" Name="lvStringTree">
<ListView.View>
<GridView>
<GridViewColumn Header="Name" Width="120" DisplayMemberBinding="{Binding Name}" />
<GridViewColumn Header="Age" Width="50" DisplayMemberBinding="{Binding Age}" />
<GridViewColumn Header="Mail" Width="150" DisplayMemberBinding="{Binding Mail}" />
</GridView>
</ListView.View>
</ListView>
<Grid x:Name="grImages" Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<ListView Grid.Row="1" Name="lvImages" ItemsSource="{Binding Path=m_ImageList}" ItemTemplate="{StaticResource itImageCell}">
<ListView.Background>
<ImageBrush/>
</ListView.Background>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="3" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
<TextBlock Name="tbImageName" Text="{Binding Path=m_ImageNames, Converter={StaticResource ResourceKey=ListToStringConverter}}" DataContext="{StaticResource itImageCell}" />
</Grid>
</Grid>
</Window>
还有我的 C# 代码
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace VirtualTrees
{
[ValueConversion(typeof(List<string>), typeof(string))]
public class ListToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (targetType != typeof(string))
throw new InvalidOperationException("The target must be a string");
return string.Join(", ", ((List<string>)value).ToArray());
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public class User
{
public string Name { get; set; }
public int Age { get; set; }
public string Mail { get; set; }
}
List<ImageSource> m_ImageList = new List<ImageSource>();
List<string> m_ImageNames = new List<string>();
string m_RegexPattern = @"\([\w ]+).(?:jpg|png)$";
public MainWindow()
{
InitializeComponent();
PopulateStringTree();
PopulateImageGrid();
}
public void PopulateStringTree()
{
List<User> vstItems = new List<User>();
for (ulong i = 0; i < 100000; ++i)
{
vstItems.Add(new User() { Name = "John Doe", Age = 42, Mail = "john@doe-family.com" });
vstItems.Add(new User() { Name = "Jane Doe", Age = 39, Mail = "jane@doe-family.com" });
vstItems.Add(new User() { Name = "Sammy Doe", Age = 7, Mail = "sammy.doe@gmail.com" });
}
lvStringTree.ItemsSource = vstItems;
}
public void PopulateImageGrid()
{
// get jpeg image file list from target dir
string moviePosterPath = @"W:\Labo\WPF\VirtualTrees\VirtualTrees\Resources\Images";
List<string> fileNames = new List<string>(System.IO.Directory.EnumerateFiles(moviePosterPath, "*.jpg"));
// iterate through files
foreach (string fileName in fileNames)
{
// load image and add it to image list
m_ImageList.Add(new BitmapImage(new Uri(fileName)));
Console.WriteLine("filename " + fileName);
// extract image file name and add it to name list
Match regexMatch = Regex.Match(fileName.Trim(), m_RegexPattern);
m_ImageNames.Add(regexMatch.Groups[1].Value);
Console.WriteLine("Movie Name: " + regexMatch.Groups[1].Value);
}
// bind data to image grid
lvImages.ItemsSource = m_ImageList;
}
}
}
你的DataTemplate
是错误的来源。您必须检查 TextBlock
的绑定。您正在绑定到 DataContext
,这是一个 BitmapSource
。 TextBlock
隐式调用 BitmapSource.ToString()
以获取该类型的字符串表示形式。 BitmapSource
已将 ToString()
覆盖为 return 完整文件路径。要解决此问题,您需要使用 IValueConverter
.
已修改 DataTemplate
。 TextBlock
绑定现在使用转换器将 BitmapSource
转换为文件名:
<DataTemplate x:Key="itImageCell">
<WrapPanel>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="100" />
<RowDefinition Height="20" />
</Grid.RowDefinitions>
<Image Width="120"
Stretch="Uniform"
Source="{Binding}" />
<TextBlock Grid.Row="1"
Width="120"
Text="{Binding ., Converter={StaticResource BitmapSourceToFilenameConverter}}"
TextTrimming="CharacterEllipsis" />
</Grid>
</WrapPanel>
</DataTemplate>
TextBlock
绑定的 IValueConverter 将 BitmapSource
转换为文件名:
[ValueConversion(typeof(BitmapSource), typeof(string))]
public class BitmapSourceToFilenameConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is BitmapSource bitmapSource)
return bitmapSource.UriSource.AbsolutePath;
return Binding.DoNothing;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
我在你的代码中发现了一个小错误:
您首先在 ListView
:
<ListView Name="lvImages" ItemsSource="{Binding Path=m_ImageList}" />
然后您覆盖(删除)它
// bind data to image grid
lvImages.ItemsSource = m_ImageList;
这不是绑定(评论不正确)。
您应该将 m_ImageList
设为 ObservableCollection<ImageSource>
而不是 List
。添加、移动或删除项目时,ObservableCollection
将自动更新 ListView
。然后从您的 MainWindow
class 中删除此行:lvImages.ItemsSource = m_ImageList;