Я нашел решение, хотя и не уверен, существует ли лучшее (я не нашел его через 3 дня). Сначала я добавил свойство зависимости (NameBindingStr), чтобы пользователи могли определять PropertyPath привязки:
public class CustomListBox : Control
{
static CustomListBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomListBox), new FrameworkPropertyMetadata(typeof(CustomListBox)));
}
public static readonly DependencyProperty NameBindingStrProperty =
DependencyProperty.Register(
"NameBindingStr", typeof(string), typeof(CustomListBox),
new FrameworkPropertyMetadata(""));
public string NameBindingStr
{
get { return (string)GetValue(NameBindingStrProperty); }
set { SetValue(NameBindingStrProperty, value); }
}
}
И XAML для пользовательского элемента управления:
<Style TargetType="{x:Type local:CustomListBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustomListBox}">
<ListBox x:Name="MainListBox" ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="true">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<local:BindTextBox TextBindingPath="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:CustomListBox}}, Path=NameBindingStr, Mode=TwoWay}"></local:BindTextBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
для привязки элементов пользовательского элемента управления я унаследовал BindTextBox от TextBox:
public class BindTextBox : TextBox
{
static BindTextBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(BindTextBox), new FrameworkPropertyMetadata(typeof(BindTextBox)));
}
public static readonly DependencyProperty TextBindingPathProperty =
DependencyProperty.Register(
"TextBindingPath", typeof(string), typeof(BindTextBox),
new FrameworkPropertyMetadata("", new PropertyChangedCallback(OnTextBindingPathChanged)));
public string TextBindingPath
{
get { return (string)GetValue(TextBindingPathProperty); }
set { SetValue(TextBindingPathProperty, value); }
}
private static void OnTextBindingPathChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
BindTextBox elem = obj as BindTextBox;
var newTextBinding = new Binding((string)args.NewValue);
newTextBinding.Mode = BindingMode.TwoWay;
BindingOperations.SetBinding(elem, TextProperty, newTextBinding);
}
}
XAML:
<Style TargetType="{x:Type local:BindTextBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:BindTextBox}">
<TextBox x:Name="TemplateTextBox" Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Text, Mode=TwoWay}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
MainWindow.xaml.cs не изменился, и я не буду вводить его снова (можно найти в вопросе). Я должен напомнить, что моей целью было позволить пользователю легко установить путь привязки. Теперь пользовательский элемент управления может использоваться одним единственным кодом:
<local:CustomListBox x:Name="BindingCustomListBox" NameBindingStr="Name"></local:CustomListBox>
Отлично работает.