【问题标题】:Two ListBoxes with two (almost similar) templates带有两个(几乎相似)模板的两个 ListBox
【发布时间】:2019-11-27 17:38:01
【问题描述】:

我有 2 个列表框和 2 个数据模板,它们几乎相同,只是其中一个包含 TextBox 而不是 ComboBox。

第一个模板:

<DataTemplate x:Key="OldPanelsTemplate" DataType="{x:Type VM:CustomPanelBoard}">
    <Grid Height="60" Margin="0">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="35"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Border Height="60" Grid.RowSpan="2" VerticalAlignment="Center" HorizontalAlignment="Center" BorderBrush="Blue" BorderThickness="1" Width="35" Background="Blue"
                Margin="0 0 2 0" >
            <TextBlock  Foreground="White" VerticalAlignment="Center" HorizontalAlignment="Center" Background="Blue" 
                        Text="{Binding RelativeSource={RelativeSource AncestorType=ListBoxItem, Mode=FindAncestor}, Path=(ItemsControl.AlternationIndex), 
                Converter={StaticResource IncrementerConverter}}" />
        </Border>
        <TextBlock Grid.Column="1" Text="{Binding Name}" />
        <TextBlock Grid.Column="2" Text="{Binding DistributionSystemName}"/>
        <TextBlock Grid.Row="1" Grid.Column="1" Text="Number of circuits to be copied: "/>
        <TextBlock Grid.Row="1" Grid.Column="2" Text="{Binding NumberOfCircuitsToBeCopied}" />
    </Grid>
</DataTemplate>

第二个模板:

<DataTemplate x:Key="NewPanelsTemplate" DataType="{x:Type VM:CustomPanelBoard}">
    <Grid Height="60">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="35"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Border Height="60" Grid.RowSpan="2" VerticalAlignment="Center" HorizontalAlignment="Center" BorderBrush="Blue" BorderThickness="1" Width="35" Background="Blue"
                Margin="0 0 2 0" >
            <TextBlock  Foreground="White" VerticalAlignment="Center" HorizontalAlignment="Center" Background="Blue" 
                        Text="{Binding RelativeSource={RelativeSource AncestorType=ListBoxItem, Mode=FindAncestor}, Path=(ItemsControl.AlternationIndex), 
                Converter={StaticResource IncrementerConverter}}" />
        </Border>
        <TextBlock Grid.Column="1" Text="{Binding Name}" />
        <ComboBox Grid.Column="2" ItemsSource="{Binding ValidDistributionSystemsForPanel}" 
                  SelectedItem="{Binding SelectedValidDistributionSystemsForPanel}" HorizontalAlignment="Stretch"  
                  IsHitTestVisible="{Binding DistributionSystemNotAssigned}" IsEnabled="{Binding DistributionSystemNotAssigned}" 
                  ItemTemplate="{StaticResource DistributionSystemTemplate}" >
        </ComboBox>
        <TextBlock Grid.Row="1" Grid.Column="1" Text="Number of available ways: "/>
        <TextBlock Grid.Row="1" Grid.Column="2" Text="{Binding NumberOfAvailableWays}" />
    </Grid>
</DataTemplate>

如您所见,除了这部分之外,它们几乎相同:

<ComboBox Grid.Column="2" ItemsSource="{Binding ValidDistributionSystemsForPanel}" 
                  SelectedItem="{Binding SelectedValidDistributionSystemsForPanel}" HorizontalAlignment="Stretch"  
                  IsHitTestVisible="{Binding DistributionSystemNotAssigned}" IsEnabled="{Binding DistributionSystemNotAssigned}" 
                  ItemTemplate="{StaticResource DistributionSystemTemplate}" >
        </ComboBox>

问题是,每当我更改其中一个中的任何内容时,我都必须更改另一个中的相同内容...任何可以以某种方式合并它们并使组合框成为根据哪个列表框更改的唯一变量正在调用模板吗?

【问题讨论】:

  • 当达到触发器内的特定条件时,您可以尝试在数据触发器内设置组合框可见性。或者为此使用转换器。您能否分享一下代码,这些模板在哪里使用?
  • 看看这个thread可能会有帮助
  • @PavelAnikhouski ...谢谢Pavel,具有切换可见性的转换器想法解决了问题..我将其作为答案发布(再次感谢)

标签: wpf data-binding listbox datatemplate listboxitem


【解决方案1】:

这是一个关于如何实现这一目标的尝试,但我不得不承认它很混乱但盲目地回答了你的问题!更好的方法是正确实现DataTemplateSelector

想法是将变化的部分解耦为两个独立的DataTemplates,并将它们放入资源中,一个用于Combobox,一个用于TextBlock

<DataTemplate x:Key="DataTemplateCombobox">
        <ComboBox  ItemsSource="{Binding ValidDistributionSystemsForPanel}" ...>
        </ComboBox>
</DataTemplate>
<DataTemplate x:Key="DataTemplateTextblock" >
        <TextBlock Text="{Binding DistributionSystemName}" ... />
</DataTemplate>

现在这些控件将被替换为您的主要(通用)DataTemplate 中的 ContentPresenterContentPresenter 使用ContentTemplateSelector 来选择要使用哪个子DataTemplate 基于应用了此DataTemplateListBox 的名称,这就是为什么Content 直接绑定到ListBox使用祖先绑定:

 <local:ValueDataTemplateSelector x:Key="TemplateSelector" 
                                     DefaultDataTemplate="{StaticResource DataTemplateTextblock}" 
                                     ComboboxDataTemplate="{StaticResource DataTemplateCombobox}" 
                                     TextBlockDataTemplate="{StaticResource DataTemplateTextblock}" />
    <DataTemplate x:Key="OldPanelsTemplate">
        <Grid Height="60" Margin="0" Name="OldPanelsTemplateGrid" >
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="35"/>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="Auto"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <Border Height="60" Grid.RowSpan="2" VerticalAlignment="Center" HorizontalAlignment="Center" BorderBrush="Blue" BorderThickness="1" Width="35" Background="Blue"
                    Margin="0 0 2 0" >
                <TextBlock  Foreground="White" VerticalAlignment="Center" HorizontalAlignment="Center" Background="Blue" Text="tex" />
            </Border>
            <TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Name}" />
            <ContentPresenter ContentTemplateSelector="{StaticResource TemplateSelector}" Content="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListBox}}}" Grid.Row="0" Grid.Column="2">

            </ContentPresenter>
            <TextBlock Grid.Row="1" Grid.Column="1" Text="Number of circuits to be copied: "/>
            <TextBlock Grid.Row="1" Grid.Column="2" Text="{Binding NumberOfCircuitsToBeCopied}" />
        </Grid>
    </DataTemplate>

您的DataTemplateSelector 应该如何实现(基本上):

public class ValueDataTemplateSelector : DataTemplateSelector
{
    public DataTemplate DefaultDataTemplate { get; set; }
    public DataTemplate ComboboxDataTemplate { get; set; }
    public DataTemplate TextBlockDataTemplate { get; set; }

    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        var lb = item as ListBox;

        if (lb is null)
            return DefaultDataTemplate;

        if (lb.Name == "ListOne")
            return ComboboxDataTemplate;
        if (lb.Name == "ListTwo")
            return TextBlockDataTemplate;
        return DefaultDataTemplate;
    }
}

最后,由于ContentPresenterContent 直接绑定到ListBox,因此您的子DataTemplates 丢失了它们的DataContext,那么只需使用ElementName 绑定或其他东西再次挂钩它们的DataContexts:

 <DataTemplate x:Key="DataTemplateCombobox">
        <ComboBox DataContext="{Binding ElementName=OldPanelsTemplateGrid, Path=DataContext}"  ItemsSource="{Binding ValidDistributionSystemsForPanel}" >
        </ComboBox>
    </DataTemplate>
    <DataTemplate x:Key="DataTemplateTextblock" >
        <TextBlock Text="{Binding DistributionSystemName}"  DataContext="{Binding ElementName=OldPanelsTemplateGrid, Path=DataContext}"/>
    </DataTemplate>

OldPanelsTemplateGrid 是主DataTemplate 中的第一个网格,它应该具有有效的ListBoxItem DataContext

这是完整的 Xaml 代码:

   </Window ...
DataContext="{Binding RelativeSource={RelativeSource Self}}"
    Title="MainWindow" Height="450" Width="800" >
<Window.Resources>
    <DataTemplate x:Key="DataTemplateCombobox">
        <ComboBox DataContext="{Binding ElementName=OldPanelsTemplateGrid, Path=DataContext}"  ItemsSource="{Binding ValidDistributionSystemsForPanel}" >
        </ComboBox>
    </DataTemplate>
    <DataTemplate x:Key="DataTemplateTextblock" >
        <TextBlock Text="{Binding DistributionSystemName}"  DataContext="{Binding ElementName=OldPanelsTemplateGrid, Path=DataContext}"/>
    </DataTemplate>
    <local:ValueDataTemplateSelector x:Key="TemplateSelector" 
                                     DefaultDataTemplate="{StaticResource DataTemplateTextblock}" 
                                     ComboboxDataTemplate="{StaticResource DataTemplateCombobox}" 
                                     TextBlockDataTemplate="{StaticResource DataTemplateTextblock}" />
    <DataTemplate x:Key="OldPanelsTemplate">
        <Grid Height="60" Margin="0" Name="OldPanelsTemplateGrid" >
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="35"/>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="Auto"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <Border Height="60" Grid.RowSpan="2" VerticalAlignment="Center" HorizontalAlignment="Center" BorderBrush="Blue" BorderThickness="1" Width="35" Background="Blue"
                    Margin="0 0 2 0" >
                <TextBlock  Foreground="White" VerticalAlignment="Center" HorizontalAlignment="Center" Background="Blue" Text="tex" />
            </Border>
            <TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Name}" />
            <ContentPresenter ContentTemplateSelector="{StaticResource TemplateSelector}" Content="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListBox}}}" Grid.Row="0" Grid.Column="2">

            </ContentPresenter>
            <TextBlock Grid.Row="1" Grid.Column="1" Text="Number of circuits to be copied: "/>
            <TextBlock Grid.Row="1" Grid.Column="2" Text="{Binding NumberOfCircuitsToBeCopied}" />
        </Grid>
    </DataTemplate>

</Window.Resources>
<!--DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorLevel=2,AncestorType=DataTemplate}}"-->
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>

    <ListBox x:Name ="ListOne" ItemsSource="{Binding MyCollection}" ItemTemplate="{StaticResource OldPanelsTemplate}"/>
    <ListBox x:Name ="LisTwo"  ItemsSource="{Binding MyCollection}" ItemTemplate="{StaticResource OldPanelsTemplate}" Grid.Row="1"/>
</Grid>

【讨论】:

  • 感谢这确实解决了问题,但作为个人喜好,我只是更喜欢转换器解决方案以避免在内容演示者中丢失数据上下文(再次感谢)
【解决方案2】:

我设法通过将它们合并到一个模板中来解决它,如下所示:

    <DataTemplate x:Key="NewOldPanelsTemplate" DataType="{x:Type VM:CustomPanelBoard}">
    <Grid Height="60">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="35"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Border Height="60" Grid.RowSpan="2" VerticalAlignment="Center" HorizontalAlignment="Center" BorderBrush="Blue" BorderThickness="1" Width="35" Background="Blue"
                Margin="0 0 2 0" >
            <TextBlock  Foreground="White" VerticalAlignment="Center" HorizontalAlignment="Center" Background="Blue" 
                        Text="{Binding RelativeSource={RelativeSource AncestorType=ListBoxItem, Mode=FindAncestor}, Path=(ItemsControl.AlternationIndex), 
                Converter={StaticResource IncrementerConverter}}" />
        </Border>
        <TextBlock Grid.Column="1" Text="{Binding Name}" />
        <!-- To have same template for new and old panels we had the two elements (combobox and textblock) for distribution system and toggle visibility by converters according to their groupbox title -->
        <ComboBox Grid.Column="2" ItemsSource="{Binding ValidDistributionSystemsForPanel}" 
                  SelectedItem="{Binding SelectedValidDistributionSystemsForPanel}" HorizontalAlignment="Stretch"  
                  IsHitTestVisible="{Binding DistributionSystemNotAssigned}" IsEnabled="{Binding DistributionSystemNotAssigned}" 
                  ItemTemplate="{StaticResource DistributionSystemTemplate}" 
                  Visibility="{Binding RelativeSource={RelativeSource AncestorType=GroupBox, Mode=FindAncestor}, Path=Header,Converter={StaticResource NewPanelsTemplateVisibilityConverter}}">
        </ComboBox>
        <TextBlock Grid.Column="2" Text="{Binding DistributionSystemName}"
                   Visibility="{Binding RelativeSource={RelativeSource AncestorType=GroupBox, Mode=FindAncestor}, Path=Header,Converter={StaticResource OldPanelsTemplateVisibilityConverter}}"/>
        <TextBlock Grid.Row="1" Grid.Column="1" Text="Number of available ways: "/>
        <TextBlock Grid.Row="1" Grid.Column="2" Text="{Binding NumberOfAvailableWays}" />
    </Grid>

</DataTemplate>

并使用这样的转换器控制可变部分(在我的情况下为 ComboBox 和 TextBlock)的可见性:

 public class OldPanelsTemplateVisibilityConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if ((string)value == "Old Panels")
        {
            return Visibility.Visible;
        }
        return Visibility.Collapsed;
    }
}


public class NewPanelsTemplateVisibilityConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if ((string)value == "Old Panels")
        {
            return Visibility.Collapsed;
        }
        return Visibility.Visible;
    }}

上面的解决方案是基于我的每个列表框都被带有特定标题的组框包围(在其他情况下,您可能希望在转换器中使用列表框名称作为可见性切换) 感谢@PavelAnikhouski 提出转换器的想法。

我还尝试了@SamTheDev 提出的 DataTemplateSelector 解决方案,它也有效(感谢@SamTheDev),但我更喜欢转换器解决方案,因为我对通过使用内容呈现器丢失数据上下文的想法感到不舒服(底部这里的行是两种解决方案的工作,没有人比这更优雅,这只是个人喜好)

【讨论】:

    猜你喜欢
    • 2015-10-17
    • 1970-01-01
    • 2014-06-26
    • 2023-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-22
    • 1970-01-01
    相关资源
    最近更新 更多