创建一个带有可展开项(手风琴)的列表框。

5

针对旧的基于XAML的UI框架(WPF/SL)有解决此问题的方案,但它们难以适用于通用Windows平台。

我正试图创建一个项目列表,在默认状态下仅显示有限详情,并在选择时展开以快速编辑一些数据。
我没有找到一种方法来创建该扩展行为,尽管它类似于Windows 10邮件应用程序中会话的展开行为。当会话中的消息被选择时,该会话的其他消息会下滑或滑动。

以下是一个示例列表,在其中我想先仅显示名称。

<ListBox ItemsSource="{x:Bind Persons}">
    <ListBox.ItemContainerStyle>
        <Style TargetType="ListBoxItem">
            <Setter Property="HorizontalContentAlignment" Value="Stretch" />
        </Style>
    </ListBox.ItemContainerStyle>
    <ListBox.ItemTemplate>
        <DataTemplate x:DataType="src:Person">
            <StackPanel HorizontalAlignment="Stretch" Width="Auto">
                <TextBlock Text="{x:Bind Path=Name, Mode=OneWay}" Margin="12, 15, 12, 0" FontSize="18.667" />
                <TextBox HorizontalAlignment="Stretch" Margin="12, 12, 12, 0" FontSize="18.667" Text="{x:Bind Path=Name, Mode=TwoWay}" />
                <TextBlock Text="Date of birth" Margin="12, 15, 12, 0" />
                <DatePicker Margin="12, 5, 12, 0" Date="{x:Bind Path=DateOfBirth, Mode=TwoWay}" />
                <TextBlock Text="Domicile" Margin="12, 15, 12, 0" />
                <TextBox Margin="12, 5, 12, 0" Text="{x:Bind Path=Domicile, Mode=OneWay}" />
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

在WPF中,这种行为可以通过触发器Style.Triggers来实现,但它们现在不再可用。
GitHub上的原始源代码:GitHub

在UWP中,您仍然可以使用触发器,并且有几种纯XAML的方法可以实现此目的。我能想到一种快速简单的方法。创建一个带有样式化ToggleButton和下面折叠的Panel的ItemTemplate。然后将面板的可见性绑定到ToggleButton的IsChecked状态,并添加一个Bool到Visibility转换器,完成。 - Chris W.
4个回答

1
这是您想要做的事情。您想使用ListViewItem.IsSelected属性,该属性由ListView本身设置。然后,您想对该值更改做出反应并设置一个可见状态,以显示或隐藏详细信息。
就像这样:
class MyModel
{
    public bool IsSelected { get; set; }
}

class MyList : Windows.UI.Xaml.Controls.ListView
{
    protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
    {
        var model = item as MyModel;
        var listViewItem = element as Windows.UI.Xaml.Controls.ListViewItem;

        var binding = new Windows.UI.Xaml.Data.Binding
        {
            Source = model,
            Mode = Windows.UI.Xaml.Data.BindingMode.TwoWay,
            Path = new PropertyPath(nameof(model.IsSelected)),
        };
        listViewItem.SetBinding(Windows.UI.Xaml.Controls.ListViewItem.IsSelectedProperty, binding);
        base.PrepareContainerForItemOverride(element, item);
    }
}

有趣的是,这段代码在某种程度上基于用于变量大小换行网格的代码。您可以在此处阅读原始文章。

http://blog.jerrynixon.com/2012/08/windows-8-beauty-tip-using.html

如果您想了解更多关于可视状态的信息,您可以阅读我在同一主题上撰写的博客文章。

http://blog.jerrynixon.com/2013/11/windows-81-how-to-use-visual-states-in.html

类似这样的:

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <Interactivity:Interaction.Behaviors>
        <Core:DataTriggerBehavior Binding="{Binding IsSelected}" Value="True">
            <Core:GoToStateAction StateName="BigVisualState"/>
        </Core:DataTriggerBehavior>
        <Core:DataTriggerBehavior Binding="{Binding IsSelected}" Value="False">
            <Core:GoToStateAction StateName="LittleVisualState"/>
        </Core:DataTriggerBehavior>
    </Interactivity:Interaction.Behaviors>

    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup x:Name="VisualStateGroup">
            <VisualState x:Name="BigVisualState"/>
            <VisualState x:Name="LittleVisualState"/>
        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>

</Grid>

如果您想了解有关Windows应用中XAML行为的更多信息,可以阅读我写的相关文章。

http://blog.jerrynixon.com/2013/10/everything-i-know-about-behaviors-in.html

我还录制了一门你可能会喜欢的课程。

https://mva.microsoft.com/en-US/training-courses/designing-your-xaml-ui-with-blend-jump-start-8260?l=p2dPykKy_5104984382

我希望这能有所帮助。

祝你好运!


0
就像Chris所说,我们可以在ViewModel中添加一个属性来控制展开行为,但这需要更改ViewModel。如果您不想这样做,这里有另一种方法:
首先,我们需要两个DataTemplate,一个用于显示简要信息,另一个用于显示详细信息。例如:
<Page.Resources>
    <!--  brief information template  -->
    <DataTemplate x:Key="ItemTemplate" x:DataType="src:Person">
        <StackPanel Width="Auto" HorizontalAlignment="Stretch">
            <TextBlock Margin="12, 15, 12, 0"
                       FontSize="18.667"
                       Text="{x:Bind Path=Name, Mode=OneWay}" />
            <TextBox Margin="12, 12, 12, 0"
                     HorizontalAlignment="Stretch"
                     FontSize="18.667"
                     Text="{x:Bind Path=Name, Mode=TwoWay}" />
        </StackPanel>
    </DataTemplate>
    <!--  details expand template  -->
    <DataTemplate x:Key="SelectedTemplate" x:DataType="src:Person">
        <StackPanel Width="Auto" HorizontalAlignment="Stretch">
            <TextBlock Margin="12, 15, 12, 0"
                       FontSize="18.667"
                       Text="{x:Bind Path=Name, Mode=OneWay}" />
            <TextBox Margin="12, 12, 12, 0"
                     HorizontalAlignment="Stretch"
                     FontSize="18.667"
                     Text="{x:Bind Path=Name, Mode=TwoWay}" />
            <StackPanel>
                <TextBlock Margin="12, 15, 12, 0" Text="Date of birth" />
                <DatePicker Margin="12, 5, 12, 0" Date="{x:Bind Path=DateOfBirth, Mode=TwoWay}" />
                <TextBlock Margin="12, 15, 12, 0" Text="Domicile" />
                <TextBox Margin="12, 5, 12, 0" Text="{x:Bind Path=Domicile, Mode=OneWay}" />
            </StackPanel>
        </StackPanel>
    </DataTemplate>
</Page.Resources>

然后在 ListBox 中将默认的 ItemTemplate 设为简要信息模板。

<ListBox ItemTemplate="{StaticResource ItemTemplate}"
         ItemsSource="{x:Bind Persons}"
         SelectionChanged="ListBox_SelectionChanged">
    <ListBox.ItemContainerStyle>
        <Style TargetType="ListBoxItem">
            <Setter Property="HorizontalContentAlignment" Value="Stretch" />
        </Style>
    </ListBox.ItemContainerStyle>
</ListBox>

最后,为SelectionChanged事件添加一个事件处理程序,在此处理程序中更改所选项目和未选项目的ContentTemplate
private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    var listBox = sender as ListBox;
    //get unselected item
    var unselectedPerson = e.RemovedItems.FirstOrDefault() as Person;
    if (unselectedPerson != null)
    {
        //get unselected item container
        var unselectedItem = listBox.ContainerFromItem(unselectedPerson) as ListBoxItem;
        //set ContentTemplate
        unselectedItem.ContentTemplate = (DataTemplate)this.Resources["ItemTemplate"];
    }
    //get selected item container
    var selectedItem = listBox.ContainerFromItem(listBox.SelectedItem) as ListBoxItem;
    selectedItem.ContentTemplate = (DataTemplate)this.Resources["SelectedTemplate"];
}

0

这里是使用MVVM的一个解决方案:

<ListBox ItemsSource="{Binding Items}" 
         SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel>
                <TextBlock Text="Title" />
                <TextBlock Text="Details" Visibility="{Binding IsSelected, Converter={StaticResource VisibilityConverter}}" />
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

public class ViewModel : BindableBase
{
    private Item _selectedItem;


    public Item[] Items { get; }

    public Item SelectedItem
    {
        get { return _selectedItem; }
        set
        {
            if (_selectedItem != null) _selectedItem.IsSelected = false;
            if (value != null) value.IsSelected = true;
            SetProperty(ref _selectedItem, value);
        }
    }
}

public class Item : BindableBase
{
    private bool _isSelected;

    public bool IsSelected
    {
        get { return _isSelected; }
        set { SetProperty(ref _isSelected, value); }
    }
}

另一个解决方案是编辑 ListBoxItem.ControlTemplate 而不是 ListBox.ItemTemplate,在这里您可以将可见性绑定到 ListBoxItem.IsSelected 属性,利用视觉状态。

0
我为UWP创建了一个可扩展的ListView控件 - 您可以在GitHub存储库这里找到它。实际上,它是WPF Expander的移植版本,我将其改编为与通用Windows平台兼容。
您可以在StackOverflow上找到我的问题和答案。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接