如何制作具有数据绑定属性的可重用WPF自定义控件?

时间:2019-03-28 20:02:03

标签: wpf data-binding custom-controls

如果我构建的自定义控件中包含一些控件(女巫也有一些绑定),那么如何从自定义控件XAML中删除绑定部分(如Text =“ {Binding Path = Name}”和ItemsSource =“ {Binding}“)使控件可重用?我的猜测是创建一些依赖项属性,但是我不知道该怎么做,让我更难的是自定义控件的DataTemplate内有一些绑定,而我无法通过GetTemplateChild()获得实例。

这是我的代码:

自定义控件:

public class CustomListBox : Control
    {
        static CustomListBox()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomListBox), new FrameworkPropertyMetadata(typeof(CustomListBox)));
        }
    }

Generics.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>
                            <TextBox x:Name="BindingTextBox" Text="{Binding Path=Name}"></TextBox>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

MainWindow.xaml:

<StackPanel>
     <local:CustomListBox x:Name="BindingCustomListBox"></local:CustomListBox>
</StackPanel>

MainWindow.xaml.cs和Person(样本数据)类:

public partial class MainWindow : Window
{
    public ObservableCollection<Person> PersonList { get; set; }
    public MainWindow()
    {
        InitializeComponent();
        PersonList = new ObservableCollection<Person>
        {
            new Person{ Name = "Person1" },
            new Person{ Name = "Person2" }
        };
        BindingCustomListBox.DataContext = PersonList;
    }
}
public class Person
{
    public string Name { get; set; }
}

通过删除绑定部分,我的意思是从自定义控件移至Window.xaml或用户希望使用该控件的任何地方。 我希望它足够清楚。

2 个答案:

答案 0 :(得分:1)

还有一个ItemsSource属性供您控制:

public class CustomListBox : Control
{
    static CustomListBox()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomListBox), new FrameworkPropertyMetadata(typeof(CustomListBox)));
    }

    public IEnumerable ItemsSource
    {
        get { return (IEnumerable)GetValue(ItemsSourceProperty); }
        set { SetValue(ItemsSourceProperty, value); }
    }

    public static readonly DependencyProperty ItemsSourceProperty =
        DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(CustomListBox));
}

在您的模板中绑定它:

<Style TargetType="{x:Type local:CustomListBox}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:CustomListBox}">
                <ListBox x:Name="MainListBox" ItemsSource="{TemplateBinding ItemsSource}" IsSynchronizedWithCurrentItem="true">
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <TextBox x:Name="BindingTextBox" Text="{Binding Name}"></TextBox>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

...并在您看来:

<local:CustomListBox x:Name="BindingCustomListBox" ItemsSource="{Binding PersonList}" />

答案 1 :(得分:0)

我找到了解决方案,但不确定是否存在更好的解决方案(三天后没有找到任何解决方案)。首先,我添加了一个依赖项属性(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>

要绑定自定义控件的项目,我从TextBox继承了BindTextBox:

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>

完美的作品。