WPF: фильтрация элементов в ItemsControl и ContentControl - PullRequest
0 голосов
/ 25 марта 2011

Я создал пользовательский элемент управления пользовательского интерфейса, который представляет представление календарного месяца. Контроль состоит из 42 границ, расположенных в сетке 7x6 (7 дней в неделю x 6 недель, чтобы показать в месяц).

Затем я создал класс Appointment. У него есть свойство DateTime AppointmentDate, которое должно определять границу в моем контроле, в которой назначение будет отображаться.

Я хочу иметь возможность предоставить набор встреч для моего элемента управления пользовательского интерфейса, а затем элемент управления должен определить, какая граница будет содержать элементы встреч, а какая останется пустой.

Каков наилучший способ достичь этого? Я думал о следующем: добавить ItemsControl к каждой границе в моем контроле, а затем привязать каждую из них к коллекции встреч. Затем я бы создал и применил фильтр к каждому из этих элементов управления ItemControls, чтобы показать или пропустить связанные встречи. Это умный, кодирование, память и производительность мудры? Есть ли лучший способ добиться этого?

Что если я хочу, чтобы на каждой границе было только одно собрание (в коллекции не будет встреч с такой же датой встречи)? Должен ли я заменить ItemsControl на ContentControl? Можно ли применить фильтрацию к ContentControls и если да, то как?

Спасибо за помощь.

Ответы [ 3 ]

2 голосов
/ 26 марта 2011

Я придумал другой подход, чем тот, который вы предлагаете.Это упрощено, и я сделал предположение или два, но давайте попробуем.

Вместо того, чтобы использовать сетку и сопоставлять дни в сетке, давайте используем WrapPanel и просто помещаем в нее дочерние элементы, каждый из которых представляетдень.

В вашем App.xaml.cs вы можете поместить некоторый код, который создаст объект Day.

public class Day
{
    public DateTime Date { get; set; }
    public List<Appointment> Appointments { get; set; }
}

public partial class App : Application
{
    protected override void OnActivated(EventArgs e)
    {
        base.OnActivated(e);

        var daysCollection = new List<Day>();
        for (int i = 1; i <= 30; i++)
        {
            daysCollection.Add(new Day
                {
                    // arbitrary sample data
                    Date = new DateTime(2011, 04, i),
                    Appointments =
                        new List<Appointment>
                            {
                                new Appointment
                                    {
                                        Date = new DateTime(2011, 04, i),
                                        Description = "Some descriptive text"
                                    }
                            }
                });
        }

        this.Properties.Add("DaysCollection", daysCollection );
    }
}

Теперь у нас есть набор дней.Встречи не важны для этой части образца.

Теперь мы создаем простой календарь UserControl и привязываем его к CalendarViewModel.

<UserControl x:Class="DaysCalendarBinding.Views.Calendar"
             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" 
             Height="210" Width="210">
    <WrapPanel x:Name="wrapPanel" Orientation="Horizontal" 
               ItemHeight="30" ItemWidth="30" 
               Loaded="wrapPanel_Loaded">
    </WrapPanel>
</UserControl>

ViewModel

public class CalendarViewModel
{
    public CalendarViewModel()
    {

    }

    public CalendarViewModel(IEnumerable<Day> inputDays)
    {
        // determine first day of the month passed in
        var firstDate =
            (from day in inputDays 
             orderby day.Date.Day 
             select day.Date).First();
        var todayIs = firstDate.DayOfWeek;
        var valueOfToday = (int) todayIs;

        // create this many blank day children
        DaysInMonth = new List<Day>();
        for (int i = valueOfToday; i > 0; i--)
        {
            // the start of some cheeze. I know. It's a sample.
            DaysInMonth.Add(new Day { Date = new DateTime(1,1,1) });
        }

        // add the rest ofthe days in to the collection
        foreach(var day in inputDays)
        {
            DaysInMonth.Add(day);
        }
    }

    public List<Day> DaysInMonth { get; private set; }
}

С помощью обработчика событий для загрузки wrapPanel

private void wrapPanel_Loaded(object sender, RoutedEventArgs e)
{
    foreach (var day in ((CalendarViewModel)DataContext).DaysInMonth)
    {
        wrapPanel.Children.Add(
                 new DayView { 
                        DataContext = new DayViewModel(day) });
    }
}

Теперь мы создаем элемент управления DayViewModel и DayView, который мы создаем и добавляем в WrapPanel.

<UserControl x:Class="DaysCalendarBinding.Views.DayView"
             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="30" d:DesignWidth="30">
    <Border BorderBrush="Black" BorderThickness="1">
        <StackPanel Height="30" Width="30" Background="AliceBlue">
            <TextBlock FontSize="7"  Text="{Binding DayDate}"/>
        </StackPanel>
    </Border> </UserControl>

ViewModel

public class DayViewModel
{
    private Day innerDay;

    public DayViewModel() {}

    public DayViewModel(Day day)
    {
        innerDay = day;
    }

    public string DayDate
    {
        get
        {
            // I know this is a cheesy approach. It's a sample. :)
            if (innerDay.Date.Year != 1)
                // this only intended to demonstrate some content
                return innerDay.Date.DayOfWeek.ToString().Remove(3) +
                       "   " + innerDay.Date.Day;
            return string.Empty;
        }
    }
}

Теперь, наконец, наше главное окно, где мы добавляем элемент управления Calendar, добавляем CalendarViewModel и, надеюсь, когда мы нажимаем F5, оно появляется для вас.:)

<Window x:Class="DaysCalendarBinding.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:Views="clr-namespace:DaysCalendarBinding.Views" Title="Calendar Demo" Height="350" Width="525">
    <Grid>
        <Views:Calendar x:Name="calendarControl"></Views:Calendar>
    </Grid>
</Window>

Кодовый код в MainWindow.xaml.cs

protected override void OnActivated(EventArgs e)
{
    base.OnActivated(e);
    calendarControl.DataContext = 
                          new CalendarViewModel((IEnumerable<Day>)Application
                              .Current
                              .Properties["DaysCollection"]);
}

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

Календарь марта

March Calendar Screenshot

Календарь апреля

April Calendar Screenshot

* 1043Теперь наступает момент, когда все это складывается так, чтобы оно работало на вас.Это просто демонстрирует технику.Представить значимый контроль не должно быть так сложно.

Приветствия.

1 голос
/ 25 марта 2011

Чтобы проконсультировать вас по первому вопросу, я бы направил вас к CollectionViewSource и упомянул, что

Когда пользователь связывает свойство WPF с коллекцией данных, WPF автоматическисоздает представление, чтобы обернуть коллекцию, и привязывает свойство к представлению, а не к необработанной коллекции. Источник

Это должно помочь вам разделить проблемы между всем вашим набором данных и видимой частью.

По второму вопросу, как только вы выбрали логику фильтрацииВы можете лучше смоделировать свой контроль, чтобы работать с ним.Поскольку я не вижу ваш код и мало знаю о том, что вы делаете, это довольно универсально.Я бы порекомендовал привязать элемент управления к одному собранию (поэтому он показывает только одно назначение, как вы просили)Если оно пустое или пустое, ничего не показывать в эту дату.Это позволит вам манипулировать данными (моделью), а не контролем (представлением), но при этом достичь желаемого результата.

0 голосов
/ 25 марта 2011

Я бы, вероятно, использовал другой подход ко всему. Я бы создал коллекцию для дней, и каждый день будет иметь целочисленное свойство для даты, недели, DayOfWeek и коллекции для встреч.

Я бы тогда создал ItemsControl, который привязан к этой коллекции.

Я бы превратил ItemsControl.PanelTemplate в сетку из 7 столбцов и 6 строк, а ItemsControl.ItemTemplate в границу, содержащую Date и ListBox или ItemsControl для хранения назначений. Я бы настроил ItemsControl.ItemStyle так, чтобы Grid.Row и Grid.Column были привязаны к Week и DayOfWeek соответственно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...