简体   繁体   English

使用ItemsControl和一个按钮的DataGridCell中的动态UI布局

[英]Dynamic UI Layout in DataGridCell using ItemsControl and a Button

在此处输入图片说明

This screenshot is from the mockup of my ideal UI. 该屏幕截图来自我理想的UI的模型。 Right now, this is a DataGridTemplateColumn , with header = "ATTENDEES". 现在,这是一个DataGridTemplateColumn ,标题为“ ATTENDEES”。 I am running into issues creating the layout of this DataGridColumn's cell. 我在创建此DataGridColumn单元格的布局时遇到问题。

I currently have an ItemsControl bound to a List of strings which are the attendees' emails. 我目前有一个ItemControl绑定到字符串列表 ,这些字符串是与会者的电子邮件。 If there are too many attendees and the ItemsControls ' bounds cannot fit in the cell, then a Button with Content = "See more" should appear at the bottom of the cell, under the last attendee email that can be rendered within in the cell's bounds. 如果参加者过多,并且ItemControls的边界无法容纳在单元格中,则该单元格的底部应显示一个Content =“ See more”的Button ,该按钮可以在该单元格的边界内呈现为最后一个参加者电子邮件。

Then once the Button ("See more") is clicked, the row should expand to an appropriate height for the attendees to all be visible, and the "See more" Button should disappear. 然后,一旦单击按钮 (“查看更多”),该行应扩展到适当的高度,以使所有人都可以看到,并且“查看更多” 按钮将消失。

I could not wrap my head around a clean implementation with a TemplateSelector , ValueConverter , or DataTrigger in pure XAML since I need to compare the ItemsControls ' height against the DataGridRow's height and then perform a modification of the cell's layout at runtime by hiding all the items in the ItemsControl that cannot fit within the cell and then showing at Button below it. 我无法用纯XAML中的TemplateSelectorValueConverterDataTrigger来实现一个干净的实现,因为我需要将ItemsControls的高度与DataGridRow的高度进行比较,然后在运行时通过隐藏所有项目来修改单元格的布局在无法容纳在单元格中的ItemsControl中 ,然后显示在其下方的Button处。

I concluded on attempting to do this in the code-behind by subscribing to the ItemControls ' load event. 我通过订阅ItemControls的load事件在后面的代码中尝试执行此操作而得出结论。 I first attempted to use the Height, MaxHeight, DesiredSize.Height, RenderedSize.Height, and ActualSize.Height properties of the ItemsControl but those all were equal to the clipped height of the ItemsControl, not the intrinsic height of all its contents. 我首先尝试使用ItemsControl的Height,MaxHeight,DesiredSize.Height,RenderedSize.Height和ActualSize.Height属性,但这些属性均等于ItemsControl的裁剪高度,而不是其所有内容的固有高度。

I am now measuring the total height of all its items' strings using the FormattedText class. 我现在使用FormattedText类测量其所有项目字符串的总高度。 Then I compare this summed height with the row's height and that's as far as I have progressed; 然后,我将求和的高度与行的高度进行比较,这是我取得的最大进展。 I am unsure of how to next change the layout of the cell or if this is even the correct approach. 我不确定下一步如何更改单元格的布局,或者这是否是正确的方法。

I feel like I am fighting against the design of the WPF framework by doing rudimentary calculations and crude layout changes to the view in the code-behind. 我觉得我正在通过对基础代码中的视图进行基本计算和粗略布局更改来与WPF框架的设计作斗争。

Any help on this would be greatly appreciated! 任何帮助,将不胜感激!


Here is my event handler for the ItemsControl.Load: 这是我的ItemsControl.Load事件处理程序:

 private void AttendeesItemsControl_Loaded(object sender, RoutedEventArgs e)
    {
        if (currentRowIndex == -1)
        {
            return;
        }

        List<ModelBase> eventsData = ModelManager.events.data;
        var eventObj = (Event)eventsData[currentRowIndex];
        var attendees = eventObj.attendees;
        var totalItemsHeight = 0;
        for(int i = 0; i < attendees.Count; i++)
        {
            totalItemsHeight += heightOfString(attendees[i]);
        }
        var itemsControl = (ItemsControl)sender;
        var controlRenderHeight = itemsControl.RenderSize.Height;
        // Check if the intrinsic height is greater than what can be drawn inside the cell
        if (controlRenderHeight < totalItemsHeight)
        {
            var itemHeight = totalItemsHeight / attendees.Count;
            var visibleItemsCount = controlRenderHeight / itemHeight;
            // .... not sure how to proceed
        }
    }

And the helper function that measures the height of one of its items: 以及辅助功能,它测量其中一项的高度:

private int heightOfString(string candidate)
    {
        var fontFamily = new FontFamily("Lato");
        var fontStyle = FontStyles.Normal;
        var fontWeight = FontWeights.Normal;
        var fontStretch = FontStretches.Normal;
        var fontSize = 12;
        var typeFace = new Typeface(fontFamily, fontStyle, fontWeight, fontStretch);
        var formattedText = new FormattedText(candidate, CultureInfo.CurrentUICulture, FlowDirection.LeftToRight, typeFace, fontSize, Brushes.Black);

        return (int)formattedText.Height;
    }

Finally, this is the DataGridTemplateColumn's XAML, with the cell template definition: 最后,这是DataGridTemplateColumn的XAML,具有单元模板定义:

<DataGridTemplateColumn Header="ATTENDEES" Width="*">
       <DataGridTemplateColumn.CellTemplate>
           <DataTemplate>
               <ItemsControl ItemsSource="{Binding Path=attendees}" x:Name="AttendeesItemsControl" Loaded="AttendeesItemsControl_Loaded">
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <TextBlock FontFamily="Lato" FontSize="12" FontWeight="Normal" Text="{Binding}">
                            </TextBlock>
                        </DataTemplate>    
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
           </DataTemplate>
       </DataGridTemplateColumn.CellTemplate>                    
   </DataGridTemplateColumn>

I had to do some real work but I got this set up. 我必须做一些实际的工作,但是我已经设置好了。 Hopefully you can follow it. 希望你能跟随它。 Here is a screen shot of what it looks like. 这是它的外观的屏幕截图。 Obviously i didn't attempt to style it yet. 显然我还没有尝试设置样式。 Just getting the resizing. 只是调整大小。 This way you let WPF handle the height of your control you leave it autosized. 这样,您就可以让WPF处理控件的高度,并使它保持自动调整大小。 You just manage your list. 您只需管理列表。

演示图片

I created a control for the list called AttendeeListControl 我为名为AttendeeListControl的列表创建了一个控件

 <UserControl xmlns:stackoverflow="clr-namespace:stackoverflow" x:Class="stackoverflow.AttendeeListControl" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"> <Grid Background="GhostWhite"> <Grid.RowDefinitions> <RowDefinition Height="37"/> <RowDefinition Height="*"/> <RowDefinition Height="23"/> </Grid.RowDefinitions> <Label Content="Attendees" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top"/> <ListBox Name="listBoxAttendees" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Row="1" /> <Button Content="SeeMore" Name="lblMore" HorizontalAlignment="Left" Margin="10,0,0,0" Grid.Row="2" VerticalAlignment="Top" Click="lblMore_Click"/> </Grid> </UserControl> 

This is the code behind 这是背后的代码

 using System.Collections.Generic; using System.Collections.ObjectModel; using System.Windows.Controls; namespace stackoverflow { /// <summary> /// Interaction logic for AttendeeListControl.xaml /// </summary> /// public partial class AttendeeListControl : UserControl { public AttendeeListViewModel vm { get; set; } public AttendeeListControl() { InitializeComponent(); var emails = new List<string>() { "email@gmail.com", "email@aol.com", "email.yahoo.com", "email@msn.com" }; var displayed = new ObservableCollection<string>() { emails[0], emails[1] }; vm = new AttendeeListViewModel() { EmailList = emails, DisplayList = displayed, Expanded = false }; DataContext = vm; listBoxAttendees.ItemsSource = vm.DisplayList; } private void lblMore_Click(object sender, System.Windows.RoutedEventArgs e) { if (vm.Expanded) { //remove all but last 2 do { vm.DisplayList.RemoveAt(vm.DisplayList.Count - 1); } while (vm.DisplayList.Count > 2); lblMore.Content = "Show More"; } else { //don't want the first 2 for (int i = 2; i < vm.EmailList.Count; i++) { vm.DisplayList.Add(vm.EmailList[i]); } lblMore.Content = "Show Less"; } vm.Expanded = !vm.Expanded; } } } 

and here is the model i used 这是我使用的模型

 using System.Collections.Generic; using System.Collections.ObjectModel; namespace stackoverflow { public class AttendeeListViewModel { public bool Expanded { get; set; } public List<string> EmailList { get; set; } public ObservableCollection<string> DisplayList { get; set; } } } 

this was all just put on the mainwindow 这些都放在主窗口中

 <Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:stackoverflow" x:Class="stackoverflow.MainWindow" Title="MainWindow" Height="350" Width="525"> <Grid> <local:AttendeeListControl HorizontalAlignment="Left" Margin="55,53,0,0" VerticalAlignment="Top"/> <local:AttendeeListControl HorizontalAlignment="Left" Margin="340,53,0,0" VerticalAlignment="Top"/> </Grid> </Window> 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM