Хотя это было решено, похоже, некоторые, на мой взгляд, ненадлежащее использование DataContext
.
При разработке пользовательского многоразового элемента управления вы не должны устанавливать DataContext
. Что будет DataContext
, то есть для пользователя пользователя элемента управления, чтобы решить, а не для разработчика. Рассмотрим следующую общую структуру кода:
<Grid DataContext="{Binding Data}">
<TextBox Text="{Binding TextValue1}" />
<!-- Some more controls -->
</Grid>
Обратите внимание, что здесь, вы с помощью элемента управления Grid
. Разработчик элемента управления (в данном случае, команда WPF) вообще не касался DataContext
- это зависит от вас. Что это значит для вас как разработчика контроля? Ваше определение DependencyProperty
в порядке, но вы не должны касаться DataContext
. Как вы тогда привяжете что-то внутри своего контроля к значению DependencyProperty
? Хороший способ использует шаблон (пространства имен опущены):
<MyTimePicker>
<MyTimePicker.Template>
<ControlTemplate TargetType="MyTimePicker">
<!-- Stuff in your control -->
<TextBlock Text="{TemplateBinding Time}" />
<TextBox Text="{Binding Time, RelativeSource={RelativeSource TemplatedParent}}" />
</ControlTemplate>
<MyTimePicker.Template>
</MyTimePicker>
Обратите внимание, что TemplateBinding
всегда только в одну стороны, так что если вам нужно любое редактирование на всех, вы должны использовать нормальное связывание (как вы можете видеть на TextBox
в примере).
Это означает, что TextBlock/Box внутри вашего элемента управления получит значение Time
из собственного пользовательского элемента управления, игнорируя любой установленный вами DataContext
.
Затем, когда вы используете контроль, вы делаете это так (добавлено к моему первому примеру):
<Grid DataContext="{Binding Data}">
<TextBox Text="{Binding TextValue1}" />
<!-- Some more controls -->
<MyTimePicker Time="{Binding TimeValue}" />
</Grid>
Что произошло здесь в том, что MyTimePicker
не DataContext
установить в любом месте на всех - это получает его от родительского элемента управления (Grid
). Таким образом, значение выглядит следующим образом: Data-->(binding)-->MyTimePicker.Time-->(template binding)-->TextBlock.Text
.
И прежде всего, избежать этого в конструкторе пользовательского элемента управления:
public MyTimePicker()
{
InitializeComponent();
DataContext = this;
}
Это отменит любые DataContext
набора в XAML, который сделает связывание огромной боли (потому что вы должны всегда установить Source
вручную). Предыдущий пример не будет работать, и это не будет работать либо:
<MyTimePicker DataContext="{Binding Data}" Time="{Binding TimeValue}" />
Вы думали бы, что это нормально, но DataContext
будет решена в InitializeComponent()
вызова, так что значение будет немедленно перезаписаны. Таким образом, привязка к TimeValue
будет искать его в контроле вместо этого (что, конечно же, потерпит неудачу).
Просто не касайтесь DataContext
при разработке элемента управления, и все будет в порядке.
На что установлен DataContext? И можете ли вы опубликовать код для класса, для которого он установлен? – Bijington
DataContext ваших элементов управления TimePicker, похоже, настроен на себя. Однако вам придется установить его в экземпляр модели представления, у которого есть свойство TimeValue, или вы установите явно источник привязки. – Clemens
Если вы устанавливаете DataContext на это (то есть элемент управления TimePicker), вам необходимо изменить привязку к Time = "{Binding Path = Time}", если вы хотите привязать его к этому свойству, иначе, поскольку Clemens заявил, что вам нужно установите DataContext в экземпляр модели представления – Bijington