Silverlight WrapPanel не растягивается вертикально внутри аккордеона

В моем приложении Silverlight есть экран меню с кнопками. Для этого я использую ItemsControl с WrapPanel в качестве шаблона ItemsPanel, поэтому кнопки переносятся на следующую строку (как текстовое поле с текстом).

Меню примерно такое:

введите описание изображения здесь

Теперь мне нужна новая функция: сгруппировать кнопки по категориям. Поэтому я решил использовать для этого Accordion с предметами ZeroOrMore. Я использую ScrollViewer как Accordion ContentTemplate с MaxHeight 150, чтобы ограничить высоту группы до 150 и использовать после этого вертикальную полосу прокрутки. Внутри ScrollViewer есть тот же ItemsControl с WrapPanel и кнопками.

Я хочу, чтобы в WrapPanel было 3 строки элементов без прокрутки, поэтому я выбрал MaxHeight = 150 не зря: каждая кнопка (с полями) имеет высоту 50. Это позволило бы иметь 3 строки без полос прокрутки. . Это прекрасно работает без аккордеона, но не работает с ним.

Проблема в том, что Accordion, похоже, не понимает высоту WrapPanel и предполагает, что он имеет высоту только 1 строки, то есть высоту 1 кнопки с полями. Итак, когда я разворачиваю элемент Accordion, он всегда имеет высоту 50. Это нормально, если у меня есть только 1 строка элементов, но если у меня 2 строки или более, высота не увеличивается до 100 или 150, она все равно 50 и полосы прокрутки появляются, так как высоты гармошки не хватает.

Вот что сейчас происходит: введите описание изображения здесь

Если я вручную установил ширину или высоту ScrollViewer или WrapPanel, проблемы не возникнет. Но это окно с изменяемым размером, и я не знаю его размера или сколько itens будет в каждой группе. Пользователь настраивает это, поэтому я не могу просто установить высоту или ширину. Аккордеон нужен мне, чтобы правильно рассчитать высоту.

Здесь для целей тестирования я установил ScrollViewer Width = 350 (немного меньше, чем ширина Accordion / UserControl, чтобы мы могли легко его увидеть), а также MaxHeight = 100 (так что для 2-й группы этого будет мало). введите описание изображения здесь

На рисунке выше показано то, что я хотел, но без ширины, чтобы пользователь мог изменять размер окна и отображать больше кнопок на экране.

Когда я разворачиваю аккордеон, я замечаю, что WrapPanel настраивает кнопки динамически, поскольку я вижу, как это происходит, я вижу, как элементы переносятся от конца первой строки к следующей. Итак, я предполагаю, что перед расширением Accordion WrapPanel не имеет ширины (возможно, это бесконечность), и поэтому она еще не обернула кнопки. Он «думает», что есть место для всех элементов в первой строке.

Ниже приведено приложение Silverlight, которое я написал, чтобы показать проблему. Файл MainPage.xaml:

    <ScrollViewer Padding="0" BorderThickness="0" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" Margin="5">
    <Grid>
        <toolkit:Accordion HorizontalAlignment="Stretch" VerticalAlignment="Top" Margin="10"
                           HorizontalContentAlignment="Left" VerticalContentAlignment="Stretch" 
                           ItemsSource="{Binding Groups}" SelectionMode="ZeroOrMore">

            <toolkit:Accordion.ItemContainerStyle>
                <Style TargetType="toolkit:AccordionItem">
                    <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
                </Style>
            </toolkit:Accordion.ItemContainerStyle>

            <toolkit:Accordion.AccordionButtonStyle>
                <Style TargetType="toolkit:AccordionButton">
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="toolkit:AccordionButton">
                                <Border Background="LightCyan" BorderBrush="Black" BorderThickness="1">
                                    <TextBlock Text="{Binding Description}"/>
                                </Border>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </toolkit:Accordion.AccordionButtonStyle>

            <toolkit:Accordion.ContentTemplate>
                <DataTemplate>
                    <Grid>
                        <ScrollViewer MaxHeight="150" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" >
                            <ItemsControl ItemsSource="{Binding Items}" >
                                <ItemsControl.ItemsPanel>
                                    <ItemsPanelTemplate>
                                        <toolkit:WrapPanel HorizontalAlignment="Left" Margin="10"/>
                                    </ItemsPanelTemplate>
                                </ItemsControl.ItemsPanel>

                                <ItemsControl.ItemTemplate>
                                    <DataTemplate>
                                        <Button HorizontalContentAlignment="Left" Margin="3" Width="195" HorizontalAlignment="Left" VerticalAlignment="Top" Content="{Binding Description}"/>
                                    </DataTemplate>
                                </ItemsControl.ItemTemplate>
                            </ItemsControl>
                        </ScrollViewer>
                    </Grid>
                </DataTemplate>
            </toolkit:Accordion.ContentTemplate>
        </toolkit:Accordion>
    </Grid>
</ScrollViewer>

И код C #: MainPage.xaml.cs с привязками свойств и кодом для добавления образцов групп / элементов:

public partial class MainPage : UserControl, INotifyPropertyChanged
{
    public class ItemViewModel
    {
        public string Description { get; set; }
    }

    public class GroupViewModel
    {
        public List<ItemViewModel> Items { get; set; }
        public string Description { get; set; }
    }

    public List<ItemViewModel> Items { get; set; }
    public List<GroupViewModel> Groups { get; set; }

    public MainPage()
    {
        InitializeComponent();
        this.DataContext = this;

        this.Groups = new List<GroupViewModel>();
        GroupViewModel g1, g2, g3;

        this.Groups.Add(g1 = new GroupViewModel() { Description = "Group with 4 itens", Items = new List<ItemViewModel>() });
        g1.Items.Add(new ItemViewModel() { Description = "item1" } );
        g1.Items.Add(new ItemViewModel() { Description = "item2" } );
        g1.Items.Add(new ItemViewModel() { Description = "item3" } );
        g1.Items.Add(new ItemViewModel() { Description = "item4" } );

        this.Groups.Add(g2 = new GroupViewModel() { Description = "Group with 10 itens", Items = new List<ItemViewModel>() });
        g2.Items.Add(new ItemViewModel() { Description = "item1" } );
        g2.Items.Add(new ItemViewModel() { Description = "item2" } );
        g2.Items.Add(new ItemViewModel() { Description = "item3" } );
        g2.Items.Add(new ItemViewModel() { Description = "item4" } );
        g2.Items.Add(new ItemViewModel() { Description = "item5" } );
        g2.Items.Add(new ItemViewModel() { Description = "item6" } );
        g2.Items.Add(new ItemViewModel() { Description = "item7" } );
        g2.Items.Add(new ItemViewModel() { Description = "item8" } );
        g2.Items.Add(new ItemViewModel() { Description = "item9" } );
        g2.Items.Add(new ItemViewModel() { Description = "item10" } );

        this.Groups.Add(g3 = new GroupViewModel() { Description = "Group with 3 itens", Items = new List<ItemViewModel>() });
        g3.Items.Add(new ItemViewModel() { Description = "item1" } );
        g3.Items.Add(new ItemViewModel() { Description = "item2" });
        g3.Items.Add(new ItemViewModel() { Description = "item3" });

        NotifyPropertyChanged("Groups");

    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

}

person Gus    schedule 13.11.2014    source источник


Ответы (2)


Я нашел решение, но мне пришлось удалить аккордеон, что для меня не проблема. Я использовал ItemsControl как «список аккордеонов» с кнопкой как AccordionButton. Остальное осталось прежним, ScrollViewer с ItemsControl и WrapPanel внутри.

ScrollViewer имеет привязку к своей высоте. У кнопки есть команда, которая изменяет высоту ScrollViewer на 0, чтобы свернуть элементы, и на NaN, чтобы развернуть. Работает безупречно.

Вот XAML:

<ItemsControl HorizontalAlignment="Stretch" VerticalAlignment="Top" ItemsSource="{Binding Groups}" 
            HorizontalContentAlignment="Left" VerticalContentAlignment="Stretch"
            ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Disabled" Margin="0">

    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
                <Grid.RowDefinitions>
                    <RowDefinition Height="24"/>
                    <RowDefinition Height="*"/>
                </Grid.RowDefinitions>

                <Button Grid.Row="0" Command="{Binding ExpandCommand}" Content="{Binding Description}"/>

                <ScrollViewer Grid.Row="1" Padding="0" VerticalScrollBarVisibility="Auto" MaxHeight="100" BorderThickness="0"
                                HorizontalScrollBarVisibility="Disabled" Height="{Binding ContentHeight}" HorizontalAlignment="Stretch">
                    <ItemsControl ItemsSource="{Binding Items}" >
                        <ItemsControl.ItemsPanel>
                            <ItemsPanelTemplate>
                                <toolkit:WrapPanel HorizontalAlignment="Left" Margin="10"/>
                            </ItemsPanelTemplate>
                        </ItemsControl.ItemsPanel>

                        <ItemsControl.ItemTemplate>
                            <DataTemplate>
                                <Button HorizontalContentAlignment="Left" Margin="3" Width="80" HorizontalAlignment="Left" VerticalAlignment="Top" Content="{Binding Description}"/>
                            </DataTemplate>
                        </ItemsControl.ItemTemplate>
                    </ItemsControl>
                </ScrollViewer>
            </Grid>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

И xaml.cs:

public partial class MainPage : UserControl, INotifyPropertyChanged
{
    public class ItemViewModel
    {
        public string Description { get; set; }
    }

    public class GroupViewModel : INotifyPropertyChanged
    {
        public GroupViewModel()
        {
            ContentHeight = double.NaN;
            NotifyPropertyChanged("ContentHeight");
            ExpandCommand = new DelegateCommand<object>(Expand);
        }
        public List<ItemViewModel> Items { get; set; }
        public string Description { get; set; }
        public double ContentHeight { get; set; }

        public DelegateCommand<object> ExpandCommand { get; set; }
        public void Expand(object obj)
        {
            if (double.IsNaN(this.ContentHeight)) this.ContentHeight = 0;
            else this.ContentHeight = double.NaN;
            NotifyPropertyChanged("ContentHeight");
        }

        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged(string propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } }
    }

    public List<ItemViewModel> Items { get; set; }
    public List<GroupViewModel> Groups { get; set; }

    public MainPage()
    {
        InitializeComponent();
        this.DataContext = this;

        this.Groups = new List<GroupViewModel>();
        GroupViewModel g1, g2, g3;

        this.Groups.Add(g1 = new GroupViewModel() { Description = "Group with 4 itens", Items = new List<ItemViewModel>() });
        g1.Items.Add(new ItemViewModel() { Description = "item1" } );
        g1.Items.Add(new ItemViewModel() { Description = "item2" } );
        g1.Items.Add(new ItemViewModel() { Description = "item3" } );
        g1.Items.Add(new ItemViewModel() { Description = "item4" } );

        this.Groups.Add(g2 = new GroupViewModel() { Description = "Group with 10 itens", Items = new List<ItemViewModel>() });
        g2.Items.Add(new ItemViewModel() { Description = "item1" } );
        g2.Items.Add(new ItemViewModel() { Description = "item2" } );
        g2.Items.Add(new ItemViewModel() { Description = "item3" } );
        g2.Items.Add(new ItemViewModel() { Description = "item4" } );
        g2.Items.Add(new ItemViewModel() { Description = "item5" } );
        g2.Items.Add(new ItemViewModel() { Description = "item6" } );
        g2.Items.Add(new ItemViewModel() { Description = "item7" } );
        g2.Items.Add(new ItemViewModel() { Description = "item8" } );
        g2.Items.Add(new ItemViewModel() { Description = "item9" } );
        g2.Items.Add(new ItemViewModel() { Description = "item10" } );

        this.Groups.Add(g3 = new GroupViewModel() { Description = "Group with 3 itens", Items = new List<ItemViewModel>() });
        g3.Items.Add(new ItemViewModel() { Description = "item1" } );
        g3.Items.Add(new ItemViewModel() { Description = "item2" });
        g3.Items.Add(new ItemViewModel() { Description = "item3" });

        NotifyPropertyChanged("Groups");

    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }


}
person Gus    schedule 14.11.2014

Я чувствую, что это не совсем ответ на вопрос, но если я удалю атрибут ScrollViewers MaxHeight и, наоборот, укажу, что Wrappanel имеет MaxWidth из 620, можно получить вертикальный вид с минимум 3 кнопками из желаемого, и аккордеон будет расти / соответственно сжимается без полосы прокрутки.

Компромисс заключается в том, что когда пользователь увеличивает ширину экрана, кнопки остаются в 3 рядах, постоянно игнорируя дополнительное горизонтальное пространство.

<toolkit:Accordion.ContentTemplate>
    <DataTemplate>
        <Grid>
            <ScrollViewer VerticalScrollBarVisibility="Auto"
                            HorizontalScrollBarVisibility="Disabled">
                <ItemsControl ItemsSource="{Binding Items}">
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <toolkit:WrapPanel HorizontalAlignment="Left"
                                                MaxWidth="620"
                                                Margin="10" />
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>

введите описание изображения здесь

person ΩmegaMan    schedule 13.11.2014
comment
Но у него не может быть максимальной ширины, это окно с изменяемым размером, максимальная ширина не позволяет эффективно использовать ширину окна. Размер окна может быть любым от 200-600 (маленькое окно с измененным размером) до 1900+ (развернутое окно на экране full hd). Кроме того, maxwidth регулирует оболочку в соответствии с ее значением ширины, изменение размера окна до меньшей ширины поместит полосы прокрутки в 3-ю группу только с 1 строкой. Но спасибо за ответ. - person Gus; 14.11.2014