我认为最简单和推荐的方法是使用ListView,其中ListView.ItemsPanel 是水平方向的StackPanel。要对齐项目并删除选择行为,将 Style 分配给 ListView.ItemContainerStyle 以禁用项目的命中测试并删除填充。
ListView.ItemTemplate 用于布局ListViewItem。
这种方法仅适用于 XAML,在布局样式和行为方面提供了最佳灵活性。
查看模型
public class ViewModel : INotifyPropertyChanged
{
public ViewModel()
{
this.Entries = new ObservableCollection<KeyValuePair<string, string>>()
{
new KeyValuePair<string, string>("string", "value1"),
new KeyValuePair<string, string>("integer", "value2"),
new KeyValuePair<string, string>("string", "value3"),
new KeyValuePair<string, string>("decimal", "value4"),
};
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
private ObservableCollection<string> entries;
public ObservableCollection<string> Entries
{
get => this.entries;
set
{
this.entries = value;
OnPropertyChanged();
}
}
}
MainWindow.xaml
<Window>
<Window.DataContext>
<local:ViewModel />
</Window.DataContext>
<Grid>
<ListView ItemsSource="{Binding Entries}">
<!-- Make the items align horizontally -->
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Padding" Value="0" />
<Setter Property="IsHitTestVisible" Value="False" />
</Style>
</ListView.ItemContainerStyle>
<!-- Layout the item -->
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock x:Name="SeparatorTextBlock" Text="," />
<TextBlock Text="{Binding Key, StringFormat=[{0}]:}" />
<TextBlock Text="[" />
<TextBlock x:Name="ValueTextBlock"
FontWeight="Bold"
Text="{Binding Value, StringFormat={}{0}}" />
<TextBlock Text="]" />
</StackPanel>
<DataTemplate.Triggers>
<!-- Set the FontWeight of the "ValueTextBlock" from bold to normal, if the Key has the value 'string' -->
<DataTrigger Binding="{Binding Key}" Value="string">
<Setter TargetName="ValueTextBlock" Property="FontWeight" Value="Normal"/>
</DataTrigger>
<!-- Remove the leading comma when the item is the first in the collection -->
<DataTrigger Binding="{Binding RelativeSource={RelativeSource PreviousData}}" Value="{x:Null}">
<Setter TargetName="SeparatorTextBlock" Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Window>
或者,您可以使用ContentPresenter 作为占位符并使用EntryToTextBlockConverter IValueConverter 将ContentPresenter.Content 绑定到Entries。布局调整必须在 C# 中完成,因此不太方便:
[ValueConversion(typeof(IEnumerable<KeyValuePair<string, string>>), typeof(TextBlock))]
public class EntriesToTextBlockConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var result = string.Empty;
if (value is IEnumerable<KeyValuePair<string, string>> entries)
{
var inlines = new List<Inline>();
entries.ToList().ForEach(
entry =>
{
inlines.Add(new Run("[" + entry.Key + "]:"));
if (entry.Key.Equals("string", StringComparison.OrdinalIgnoreCase))
inlines.Add(new Run("[" + entry.Value + "]"));
else
{
inlines.Add(new Run("["));
inlines.Add(new Bold(new Run("[" + entry.Value + "]")));
inlines.Add(new Run("]"));
}
inlines.Add(new Run(","));
});
inlines.RemoveAt(inlines.Count - 1);
var textBlock = new TextBlock();
textBlock.Inlines.AddRange(inlines);
return textBlock;
}
return Binding.DoNothing;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
MainWindow.xaml
<Window>
<Window.DataContext>
<local:ViewModel />
</Window.DataContext>
<Window.Ressources>
<local:EntriesToTextBlockConverter x:Key="EntriesToTextBlockConverter" />
</Window.Ressources>
<Grid>
<ContentPresenter Content="{Binding Entries, Converter={StaticResource EntriesToTextBlockConverter}}">
</Grid>
</Window>