powered by simpleCommunicator - 2.0.51     © 2025 Programmizd 02
Форумы / WPF, Silverlight [игнор отключен] [закрыт для гостей] / Связать DependencyProperty с содержимым UserControl-а
9 сообщений из 9, страница 1 из 1
Связать DependencyProperty с содержимым UserControl-а
    #39005791
SunRise1008
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Всем привет!
Я хочу создать контрол, где можно выбрать дату с помощью DatePicker-а, а часы и минуты - это 2 комбобокса.
И чтобы эта дата-время было свойством зависимости.

Код: xml
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
<UserControl x:Class="***.Controls.DateTimeControl"
             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" DataContext="{Binding RelativeSource={RelativeSource Self}}">
    <Grid MinWidth="250">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <TextBlock Grid.Row="0" Grid.Column="0" Text="{x:Static resx:Resources.LabelDate}" Style="{StaticResource TitleTextBlockStyle}" Margin="5"/>
        <DatePicker Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="3" Style="{StaticResource CustomDatePickerStyle}" Margin="2" SelectedDate="{Binding Date, Mode=TwoWay}"/>
        <TextBlock Grid.Row="1" Grid.Column="0" Text="{x:Static resx:Resources.LabelHours}" Style="{StaticResource TitleTextBlockStyle}" Margin="5"/>
        <ComboBox Grid.Row="1" Grid.Column="1" Style="{StaticResource CustomComboBoxStyle}" Margin="2" ItemsSource="{Binding Hours}" SelectedItem="{Binding Hour, Mode=TwoWay}"/>
        <TextBlock Grid.Row="1" Grid.Column="2" Text="{x:Static resx:Resources.LabelMinutes}" Style="{StaticResource TitleTextBlockStyle}" Margin="5"/>
        <ComboBox Grid.Row="1" Grid.Column="3" Style="{StaticResource CustomComboBoxStyle}" Margin="2" ItemsSource="{Binding Minutes}" SelectedItem="{Binding Minute, Mode=TwoWay}"/>
    </Grid>
</UserControl>



Вот xaml.cs файл. Собственно свойство зависимости:SelectedDate и SelectedDatePropertyChangedCallback, который устанавливает выбранные значения для всех контролов. Hours, Minutes - это коллекции источники для комбо-боксов, а Date, Hour, Minute - выбранные элементы.

Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
    public partial class DateTimeControl : INotifyPropertyChanged
    {
        public static readonly DependencyProperty SelectedDateProperty =
            DependencyProperty.Register(
                "SelectedDate",
                typeof (DateTime?),
                typeof (DateTimeControl),
                new FrameworkPropertyMetadata(null, SelectedDatePropertyChangedCallback));

        public DateTime? SelectedDate
        {
            get { return (DateTime?) GetValue(SelectedDateProperty);}
            set { SetValue(SelectedDateProperty, value); }
        }

        private static void SelectedDatePropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
        {
            var dateTimeControl = (DateTimeControl) dependencyObject;
            var newDateTime = (DateTime?) dependencyPropertyChangedEventArgs.NewValue;
            if (newDateTime == null)
            {
                dateTimeControl.Date = null;
                dateTimeControl.Hour = null;
                dateTimeControl.Minute = null;
            }
            else
            {
                dateTimeControl.Date = newDateTime;
                dateTimeControl.Hour = ((DateTime)newDateTime).Hour;
                dateTimeControl.Minute = (int)Math.Round(((DateTime)newDateTime).Minute / 5.0) * 5;
            }
        }

        public DateTime? Date
        {
            get
            {
                return _date;
            }
            set
            {
                _date = value;
                OnPropertyChanged();
            }
        }
        private DateTime? _date;

        public int? Hour
        {
            get
            {
                return _hour;
            }
            set
            {
                _hour = value;
                OnPropertyChanged();
            }
        }
        private int? _hour;

        public int? Minute
        {
            get
            {
                return _minute;
            }
            set
            {
                _minute = value;
                OnPropertyChanged();
            }
        }
        private int? _minute;

        public ObservableCollection<int> Hours
        {
            get
            {
                return _hours;
            }
            set
            {
                _hours = value;
                OnPropertyChanged();
            }
        }
        private ObservableCollection<int> _hours;

        public ObservableCollection<int> Minutes
        {
            get
            {
                return _minutes;
            }
            set
            {
                _minutes = value;
                OnPropertyChanged();
            }
        }
        private ObservableCollection<int> _minutes;

        public DateTimeControl()
        {
            InitializeComponent();

            Hours = new ObservableCollection<int>();
            for (int i = 0; i < 24; i++)
                Hours.Add(i);

            Minutes = new ObservableCollection<int>();
            for (int i = 0; i < 60; i += 5)
                Minutes.Add(i);
        }

        public event PropertyChangedEventHandler PropertyChanged;

        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }



Собственно вопрос: что нужно сделать, чтобы при изменении часов и минут, (когда пользователь выставляет другое время ) обновлялось значение SelectedDateProperty?
...
Рейтинг: 0 / 0
Связать DependencyProperty с содержимым UserControl-а
    #39005926
SunRise1008
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
В общем, решение нашел. Не знаю насколько это правильно. Буду безумно благодарен, если кто-то пнет и скажет как сделать лучше.

В общем в каждом свойстве Date, Hour, Minute в сеттере устанавливаю значение SelectedDate + фиксанул SelectedDatePropertyChangedCallback.

Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
public partial class DateTimeControl : INotifyPropertyChanged
    {
        public static readonly DependencyProperty SelectedDateProperty =
            DependencyProperty.Register(
                "SelectedDate",
                typeof (DateTime?),
                typeof (DateTimeControl),
                new FrameworkPropertyMetadata(null, SelectedDatePropertyChangedCallback));

        public DateTime? SelectedDate
        {
            get { return (DateTime?) GetValue(SelectedDateProperty);}
            set { SetValue(SelectedDateProperty, value); }
        }

        private static void SelectedDatePropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
        {
            if (dependencyPropertyChangedEventArgs.OldValue == dependencyPropertyChangedEventArgs.NewValue)
                return;

            var dateTimeControl = (DateTimeControl) dependencyObject;
            var newDateTime = (DateTime?) dependencyPropertyChangedEventArgs.NewValue;
            if (newDateTime == null)
            {
                dateTimeControl.Date = null;
                dateTimeControl.Hour = null;
                dateTimeControl.Minute = null;
            }
            else
            {
                dateTimeControl.Date = ((DateTime)newDateTime).Date;
                dateTimeControl.Hour = ((DateTime)newDateTime).Hour;
                dateTimeControl.Minute = (int)Math.Round(((DateTime)newDateTime).Minute / 5.0) * 5;
            }
        }

        public DateTime? Date
        {
            get
            {
                return _date;
            }
            set
            {
                _date = value;
                OnPropertyChanged();

                if (Date == null)
                {
                    SelectedDate = null;
                    return;
                }

                SelectedDate = ((DateTime)Date).AddHours(Hour ?? 0).AddMinutes(Minute ?? 0);
            }
        }
        private DateTime? _date;

        public int? Hour
        {
            get
            {
                return _hour;
            }
            set
            {
                _hour = value;
                OnPropertyChanged();

                SelectedDate = (Date ?? DateTime.Today).AddHours(Hour ?? 0).AddMinutes(Minute ?? 0);
            }
        }
        private int? _hour;

        public int? Minute
        {
            get
            {
                return _minute;
            }
            set
            {
                _minute = value;
                OnPropertyChanged();

                SelectedDate = (Date ?? DateTime.Today).AddHours(Hour ?? 0).AddMinutes(Minute ?? 0);
            }
        }
        private int? _minute;

        public ObservableCollection<int> Hours
        {
            get
            {
                return _hours;
            }
            set
            {
                _hours = value;
                OnPropertyChanged();
            }
        }
        private ObservableCollection<int> _hours;

        public ObservableCollection<int> Minutes
        {
            get
            {
                return _minutes;
            }
            set
            {
                _minutes = value;
                OnPropertyChanged();
            }
        }
        private ObservableCollection<int> _minutes;

        public DateTimeControl()
        {
            InitializeComponent();

            Hours = new ObservableCollection<int>();
            for (int i = 0; i < 24; i++)
                Hours.Add(i);

            Minutes = new ObservableCollection<int>();
            for (int i = 0; i < 60; i += 5)
                Minutes.Add(i);
        }

        public event PropertyChangedEventHandler PropertyChanged;

        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
...
Рейтинг: 0 / 0
Связать DependencyProperty с содержимым UserControl-а
    #39006348
Roman Mejtes
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
лично я тоже свой datepicker делал
делал след. образом.
делал не UserControl, а CustomControl, так как UserControl наследник ContentPresenter'а, для этого контрола мне не нужен контент поле.
Идеально подходит класс наследованный от Control.
начал с NumberPicker, это миниконтрол который принимает фокус, обрабатывает нажатия только цифровых клавиш, в котором определено максимальное, минимальное значение, количество символов и дальше по вкусу.
Переопределяем основные метаданные свойств зависимостей, фокус по умолчанию, стиль по умолчанию и т.д.
Задаем поведение, при вводе N символов, передаем фокус на след. элемент. Таким образом при вводе 01022014, ввод будет непрерывным. После смены фокуса ввод осуществляется по новой.
Важен контроль значений, 0 можно ввести, но дня 0 не бывает. По этому идеальный вариант будет изменять значение после потери фокуса, а не в момент ввода.
Делаем шаблон для этого контрола обычный TextBlock и привязываем его к Value, в последствии я сделал отдельное поле DisplayValue, чтоб была большая гибкость по настройки отображения.
Добавляем поведение на нажатие стрелок вверх, вниз (влево\вправо будет работать на FocusManager'е сам) и добавляем прокрутку колёсиком.

Затем создал 2 контрол DatePicker , в нем определил поле CurrentDate и отдельные свойства для дня, месяца, года, часа и минут. Сунул их в отдельный класс, там же свойства максимального значения для дня, месяца и года (так как есть еще интервал выбора).
Соответственно, если вводить 31.02, то после ввода месяца, значение MaxDay уменьшится до 28(29), а через CourceValue свойства зависимости в 1 контроле, оно преобразится в 28, так же действуем с остальными датами.
для Calendar можно просто скопипастить кнопку с шаблона DatePicker'а, либо сделать своё. Прикрутить в шаблон Popup и Calendar'ь в ней.
Создавая шаблон важно учитывать, что дата может быть разных форматов в зависимости от локализации. Значит контрол NumberPicker должны располагаться по разному
Тут есть 3 пути (на мой взгляд):
1) создаем 3 разновидности шаблона, для разных вариантов типа : DD/MM/YYYY, MM/DD/YYYY и YYYY-MM-DD, создаем свойство зависимости в котором будет определять какой вид отображения имеет дата и в стиле через триггер задает все 3 шаблона.
2) создаем эти объекты динамически через Children, но тогда мы теряем магию WPF, придется зафигачить массу кода, с анимацией будут геморои
3) разместить их в Grid'е и свойство Grid.Column, задать для каждого контрола в параметрах что отображает этот контрол, а конвертер сам всё определит.
Определял я через ShortDate из Globalization.


как то так делал, получилось довольно удобно для ввода в числах.
...
Рейтинг: 0 / 0
Связать DependencyProperty с содержимым UserControl-а
    #39006466
Фотография Алексей К
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
...
Рейтинг: 0 / 0
Связать DependencyProperty с содержимым UserControl-а
    #39006695
Roman Mejtes
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Алексей К,

проблема в TextBox заключается в том, что ограничения ввода в TextBox невозможно сделать нормальным способом, надо учитывать массу нюансов, обрабатывать множество устройств ввода и т.д. добиться поведения, при котором вводится будет только дата в заданном формате не возможно, а 1 из требования может быть именно это. Не DatePicker, не DateTimePicker такого не позволит.
...
Рейтинг: 0 / 0
Связать DependencyProperty с содержимым UserControl-а
    #39006781
SunRise1008
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Roman Mejtes,

Тут, в принципе, не было задачи обеспечить быстрый и безопасный ручной ввод. Даты выбираются на ближайшие дни, поэтому пикер, как мне кажется, удобнее. Хотя дальше будет видно )))
Мне интересно, насколько правильно сделан контрол? Не считая не самый лучший выбор родителя.
...
Рейтинг: 0 / 0
Связать DependencyProperty с содержимым UserControl-а
    #39006901
Фотография Алексей К
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Roman MejtesАлексей К,

проблема в TextBox заключается в том, что ограничения ввода в TextBox невозможно сделать нормальным способом, надо учитывать массу нюансов, обрабатывать множество устройств ввода и т.д. добиться поведения, при котором вводится будет только дата в заданном формате не возможно, а 1 из требования может быть именно это. Не DatePicker, не DateTimePicker такого не позволит.Лучше не ограничивать ввод и применить валидацию родных биндингов с соответствующей индикацией.
...
Рейтинг: 0 / 0
Связать DependencyProperty с содержимым UserControl-а
    #39006970
Roman Mejtes
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Алексей К,

это вам как программисту лучше, мне тоже мягко говоря насрать ограничен ввод или нет. Такова и вся "парадигма" ввода и валидации в WPF, вводим, что угодно, а на уровне связывания делаем валидацию и актуализируем её в Validation.Template, но вот у дизайнеров и заказчиков по этому поводу другое мнение может быть и приходится вообще исключаться TextBox из этой схемы, так как он совершенно не подходит в таком случае :(
...
Рейтинг: 0 / 0
Связать DependencyProperty с содержимым UserControl-а
    #39007829
Фотография Алексей К
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Roman Mejtes... но вот у дизайнеров и заказчиков по этому поводу другое мнение ...И они готовы детально описать плюсы и минусы обоих подходов, чтобы обосновать свой выбор?

Мне кажется, что "заказчик всегда прав" не аргумент для технического форума.
...
Рейтинг: 0 / 0
9 сообщений из 9, страница 1 из 1
Форумы / WPF, Silverlight [игнор отключен] [закрыт для гостей] / Связать DependencyProperty с содержимым UserControl-а
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]