Постоянная ширина элемента по горизонтали ItemControl - PullRequest
0 голосов
/ 30 сентября 2011

Мне нужна горизонтально организованная ItemsControl, которая ограничивает все свои элементы одинаковой шириной. Я использую элементы UserControl s и строит TextBlock с автоматическим размером, показывающий значение int (содержащееся в свойстве зависимостей) с Border вокруг него. Проблема в том, что меньшие значения приводят к более узкому элементу, и мне нужно, чтобы все элементы были одинаковыми. Я рассмотрел несколько решений, но, похоже, я не могу заставить их работать.

Первый - установить для ItemsPanel шаблона значение Grid. Таким образом, я могу использовать выделенный код для генерации необходимого количества столбцов на основе источника данных и установить для всех значений ширины столбцов значение *. Загадка заключалась в том, чтобы выяснить, как установить свойство столбца сетки для каждого элемента.

Второе решение - установить для ItemsPanel шаблона значение StackPanel. Это автоматически упорядочивает элементы правильно, но я не могу установить ширину каждого элемента равной ширине самого широкого элемента.

Последнее решение - установить для ItemsPanel шаблона значение UniformGrid с кодом для установки количества столбцов при каждом изменении источника данных. Это автоматически расположит элементы правильно и обеспечит равномерную ширину. Моя проблема здесь в том, что я не могу заставить что-либо показывать. Я попытался вручную добавить кнопки, и они показывают нормально. Мой UserControl не появится. Я перечислил это решение ниже.

    <Window x:Class="Learning_WPF.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:my="clr-namespace:Learning_WPF"
            xmlns:sys="clr-namespace:System;assembly=mscorlib"
            Title="DateTape" Height="176" Width="500">
        <Window.Resources>
            <my:DateList x:Key="dateList" CollectionChanged="DateList_CollectionChanged" />
        </Window.Resources>
        <ItemsControl x:Name="itemsControl1" ItemsSource="{Binding Source={StaticResource dateList}, Path=/}" Grid.Row="1">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <UniformGrid x:Name="daysGrid" Rows="1" />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>
    </Window>

    public partial class MainWindow : Window
    {
        UniformGrid daysGrid;
        DateList dateList;

        public MainWindow()
        {
            InitializeComponent();
            daysGrid = (UniformGrid)itemsControl1.ItemsPanel.LoadContent();
            dateList = (DateList)FindResource("dateList");
            dateList.Fill(DateTime.Today, DateTime.Today.AddDays(10));
        }

        private void DateList_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            daysGrid.Columns = dateList.Count;
        }

    }

    public class DateList : ObservableCollection<Date>
    {
        public void Fill(DateTime first, DateTime last)
        {
            // implementation fills the array with all of the days between first and last, inclusively
        }
    }

Может быть, есть лучший способ сделать то, что я хочу достичь (возможно, немного графически, чем с помощью элементов управления) ...

Ответы [ 2 ]

1 голос
/ 30 сентября 2011

Если UniformGrid не подойдет для вашего решения, вы всегда можете попытаться обойтись установкой ItemContainerStyle для ItemsControl (при условии, что вы можете использовать ListBox с ListBoxItem детьми)вот так:

<Setter Property="ItemContainerStyle">
    <Setter.Value>
        <Style TargetType="{x:Type ListBoxItem}" >
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ListBoxItem}">
                        <ContentPresenter Width="75" ... />
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Setter.Value>
</Setter>
0 голосов
/ 30 сентября 2011

Я наконец заставил его работать, используя Grid.Мне пришлось сделать какой-то значительный код, и интерфейс заметно замедлился из-за тройного макета, но он работает.Предложения по улучшению этого приветствуются.При вставке этого элемента управления в окно измените свойство StartDate со значения по умолчанию ДО свойства EndDate;в противном случае VS заблокируется.

XAML

    <UserControl x:Class="MyProject.DateTape"
                 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"
                 xmlns:my="clr-namespace:MyProject"
                 xmlns:sys="clr-namespace:System;assembly=mscorlib"
                 mc:Ignorable="d" 
                 d:DesignHeight="45" d:DesignWidth="188">
        <UserControl.Resources>
            <Thickness x:Key="bottomThickness" Bottom="1" Top="1" Left="0.5" Right="0.5" />
            <Thickness x:Key="topThickness" Bottom="0" Top="1" Left="0.5" Right="0.5" />
            <Style TargetType="Border">
                <Setter Property="BorderBrush" Value="#FFBEBEBE"/>
            </Style>
            <Style TargetType="TextBlock">
                <Setter Property="TextAlignment" Value="Center"/>
                <Setter Property="Margin" Value="6,0"/>
            </Style>
        </UserControl.Resources>
        <Grid x:Name="mainGrid"/>
    </UserControl>

C #

        public partial class DateTape : UserControl
        {
            public static readonly DependencyProperty StartDateProperty =
                DependencyProperty.Register("StartDate", typeof(DateTime), typeof(DateTape),
                                            new PropertyMetadata(new PropertyChangedCallback(OnDatesChanged)));
            public static readonly DependencyProperty EndDateProperty =
                DependencyProperty.Register("EndDate", typeof(DateTime), typeof(DateTape),
                                            new PropertyMetadata(new PropertyChangedCallback(OnDatesChanged)));

            [Category("Common"),
             TypeConverter(typeof(DateConverter))]
            public DateTime StartDate
            {
                get { return (DateTime)GetValue(StartDateProperty); }
                set { SetValue(StartDateProperty, value); }
            }
            [Category("Common"),
             TypeConverter(typeof(DateConverter))]
            public DateTime EndDate
            {
                get { return (DateTime)GetValue(EndDateProperty); }
                set { SetValue(EndDateProperty, value); }
            }

            public DateTape()
            {
                InitializeComponent();
            }

            private void Layout()
            {
                int dayCount = (EndDate - StartDate).Days;
                double max = 0;
                DateTime current = StartDate;
                ColumnDefinition columnDefinition;
                Border border;
                TextBlock textBlock;
                Binding binding;
                Size infinity = new Size(double.PositiveInfinity, double.PositiveInfinity);
                Thickness bottomThickness = (Thickness)Resources["bottomThickness"],
                          topThickness = (Thickness)Resources["topThickness"];
                mainGrid.ColumnDefinitions.Clear();
                for (int i = 0; i <= dayCount; i++, current += TimeSpan.FromDays(1))
                {
                    mainGrid.ColumnDefinitions.Add(columnDefinition = new ColumnDefinition());
                    // Add a day
                    border = new Border();
                    textBlock = new TextBlock();
                    textBlock.Text = current.Day.ToString();
                    border.Child = textBlock;
                    binding = new Binding();
                    binding.Source = bottomThickness;
                    border.SetBinding(Border.BorderThicknessProperty, binding);
                    mainGrid.Children.Add(border);
                    border.Measure(infinity);
                    max = Math.Max(max, border.DesiredSize.Width);
                    Grid.SetRow(border, 2);
                    Grid.SetColumn(border, i);
                }
                foreach (ColumnDefinition cd in mainGrid.ColumnDefinitions)
                {
                    cd.MinWidth = max;
                }
            }

            private static void OnDatesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                (d as DateTape).Layout();
            }

Редактировать:

Я нашел способ сделать это без использованиянесколько проходов макета путем запроса DesiredSize каждого элемента Border, когда я добавляю их в Grid, и отслеживаю максимальную ширину.

...