问题描述
我有两个表,其中也只包含一个作为键的值。 我创建了一个列表框来显示和修改它。
- 表 1 为
TTypes1
,字段为Type1
字符串 - 表 2 为
TTypes2
,字段为Type2
字符串
我写了这个DataTemplate
:
<DataTemplate x:Key="ListBoxItems">
<Grid>
<Grid.ColumnDeFinitions>
<ColumnDeFinition Width="*" />
<ColumnDeFinition Width="Auto" />
</Grid.ColumnDeFinitions>
<Label Grid.Column="0" Content="{Binding}" />
<StackPanel Grid.Column="1" Orientation="Horizontal">
<!-- edit to swap with save -->
<Button
Content=""
ContentTemplate="{StaticResource IconPanelButton}"
DockPanel.Dock="Right"
Style="{StaticResource MahApps.Styles.Button.Flat}"
ToolTipService.ToolTip="Save"
Visibility="{Binding IsVisible}" />
<!-- Cancel - visible only on edit -->
<Button
Click="LockUnlock_Click"
Content="{Binding Icon}"
ContentTemplate="{StaticResource IconPanelButton}"
DockPanel.Dock="Right"
Style="{StaticResource MahApps.Styles.Button.Flat}"
ToolTipService.ToolTip="{Binding ToolTip}" />
<!-- Delete -->
<Button
Click="LockUnlock_Click"
Content="{Binding Icon}"
ContentTemplate="{StaticResource IconPanelButton}"
DockPanel.Dock="Right"
Style="{StaticResource MahApps.Styles.Button.Flat}"
ToolTipService.ToolTip="{Binding ToolTip}" />
<!-- Add -->
<Button
Click="LockUnlock_Click"
Content="{Binding Icon}"
ContentTemplate="{StaticResource IconPanelButton}"
DockPanel.Dock="Right"
Style="{StaticResource MahApps.Styles.Button.Flat}"
ToolTipService.ToolTip="{Binding ToolTip}" />
</StackPanel>
</Grid>
</DataTemplate>
这是列表框,但我无法让它按我的意愿工作。
如果我这样离开:
<Label Grid.Column="0" Content="{Binding}" />
我没有看到文本,但看到的是 TTypes1
或 TTypes2
类型。
但如果我写:
<Label Grid.Column="0" Content="{Binding Type1}" />
那么我不能在 TType2
列表框中使用它。
这里是我使用它的地方:
<ScrollViewer
Margin="2"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto">
<ListBox
Margin="2"
AlternationCount="2"
BorderThickness="1"
ItemsSource="{Binding TTypes1}"
Selectedindex="0"
SelectionMode="Single"
ItemTemplate="{StaticResource ListBoxItems}"
Style="{StaticResource MahApps.Styles.ListBox.Virtualized}">
</ListBox>
</ScrollViewer>
第二个是:
<ScrollViewer
Margin="2"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto">
<ListBox
Margin="2"
AlternationCount="2"
BorderThickness="1"
ItemsSource="{Binding TTypes2}"
Selectedindex="0"
SelectionMode="Single"
ItemTemplate="{StaticResource ListBoxItems}"
Style="{StaticResource MahApps.Styles.ListBox.Virtualized}">
</ListBox>
</ScrollViewer>
我错过了什么?
解决方法
多个数据模板
通常的处理方法是为每种类型创建一个不同的数据模板,例如TType1
和 TType2
。
<DataTemplate x:Key="ListboxItemsTType1"
DataType="{x:Type local:TType1}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0"
Content="{Binding Type1}" />
<!-- ...other markup. -->
</Grid>
</DataTemplate>
<DataTemplate x:Key="ListboxItemsTType2"
DataType="{x:Type local:TType2}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0"
Content="{Binding Type2}" />
<!-- ...other markup. -->
</Grid>
</DataTemplate>
在您的ListBox
es 中引用特定模板。您还可以从数据模板中删除 x:Key
,因此它们会自动应用于 ListBox
中的匹配类型。这也适用于列表中的混合项目。
<ScrollViewer Grid.Row="0"
Margin="2"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto">
<ListBox
ItemTemplate="{StaticResource ListboxItems}"
...
</ListBox>
</ScrollViewer>
<ScrollViewer Grid.Row="1"
Margin="2"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto">
<ListBox
...
ItemTemplate="{StaticResource ListboxItems}"
</ListBox>
</ScrollViewer>
其他方法
如果您真的想保留单个数据模板,则必须根据绑定为数据上下文的对象的项目类型来切换绑定。有多种方法可以实现这一点。
这是一个使用 converter that converts an object to its type 中的 related question 的示例,请复制它。 Label
的样式将使用数据触发器根据该类型应用正确的绑定。
<local:DataTypeConverter x:Key="DataTypeConverter" />
<DataTemplate x:Key="ListboxItems">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0">
<Label.Style>
<Style TargetType="{x:Type Label}"
BasedOn="{StaticResource {x:Type Label}}">
<Setter Property="Content"
Value="{x:Null}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Converter={StaticResource DataTypeConverter}}"
Value="{x:Type local:TType1}">
<Setter Property="Content"
Value="{Binding Type1}" />
</DataTrigger>
<DataTrigger Binding="{Binding Converter={StaticResource DataTypeConverter}}"
Value="{x:Type local:TType2}">
<Setter Property="Content"
Value="{Binding Type2}" />
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
<StackPanel Grid.Column="1"
Orientation="Horizontal">
<!-- edit to swap with save -->
<Button Content=""
ContentTemplate="{StaticResource IconPanelButton}"
DockPanel.Dock="Right"
Style="{StaticResource MahApps.Styles.Button.Flat}"
ToolTipService.ToolTip="Save"
Visibility="{Binding IsVisible}" />
<!-- Cancel - visible only on edit -->
<Button Click="LockUnlock_Click"
Content="{Binding Icon}"
ContentTemplate="{StaticResource IconPanelButton}"
DockPanel.Dock="Right"
Style="{StaticResource MahApps.Styles.Button.Flat}"
ToolTipService.ToolTip="{Binding ToolTip}" />
<!-- Delete -->
<Button Click="LockUnlock_Click"
Content="{Binding Icon}"
ContentTemplate="{StaticResource IconPanelButton}"
DockPanel.Dock="Right"
Style="{StaticResource MahApps.Styles.Button.Flat}"
ToolTipService.ToolTip="{Binding ToolTip}" />
<!-- Add -->
<Button Click="LockUnlock_Click"
Content="{Binding Icon}"
ContentTemplate="{StaticResource IconPanelButton}"
DockPanel.Dock="Right"
Style="{StaticResource MahApps.Styles.Button.Flat}"
ToolTipService.ToolTip="{Binding ToolTip}" />
</StackPanel>
</Grid>
</DataTemplate>
其他完全依赖代码但更容易重用的选项是:
- 创建一个与触发器相同的特殊值转换器,返回在代码中创建的绑定,其属性路径基于类型
- 创建自定义标记扩展,根据类型自动选择属性路径
我不提供有关这些选项的示例,因为它们很复杂并且在很大程度上取决于您的要求。此外,我推荐第一种方法来创建多个数据模板,因为我认为从维护和灵活性的角度来看,这是最有利的。
,如果您喜欢两个数据模板,我认为最好使用 ItemsControl.ItemTemplateSelector。 首先,您需要一个类继承类“DataTemplateSelector”并覆盖其方法以选择要使用的数据模板。
public class ModelItemTemplateSelector: DataTemplateSelector
{
public DataTemplate Model1Template { get; set; }
public DataTemplate Model2Template { get; set; }
public override DataTemplate SelectTemplate(object item,DependencyObject container)
{
if(item is Model1)
{
return Model1Template;
}
else if(item is Model2)
{
return Model2Template;
}
return base.SelectTemplate(item,container);
}
}
然后xaml中的代码如下
<ListBox ItemsSource="{Binding Source}">
<ListBox.ItemTemplateSelector>
<local:ModelItemTemplateSelector Model1Template="{StaticResource Model1Template}" Model2Template="{StaticResource Model2Template}" />
</ListBox.ItemTemplateSelector>
</ListBox>
以及其他代码:
两个数据模板
<DataTemplate x:Key="Model1Template" DataType="{x:Type local:Model1}">
<TextBlock Text="{Binding Age}" />
</DataTemplate>
<DataTemplate x:Key="Model2Template" DataType="{x:Type local:Model2}">
<TextBlock Text="{Binding Name}" />
</DataTemplate>
两种类型
public class BaseModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
this.PropertyChanged?.Invoke(this,new PropertyChangedEventArgs(propertyName));
}
}
public class Model1 : BaseModel
{
private int age;
public int Age
{
get { return age; }
set
{
age = value;
this.RaisePropertyChanged(nameof(Age));
}
}
}
public class Model2 : BaseModel
{
private string name;
public string Name
{
get { return name; }
set
{
name = value;
this.RaisePropertyChanged(nameof(Name));
}
}
}
虚拟机中的源
private ObservableCollection<BaseModel> source;
public ObservableCollection<BaseModel> Source
{
get { return source; }
set
{
source = value;
this.RaisePropertyChanged(nameof(Source));
}
}