Как создать многоразовый пользовательский элемент управления WPF со свойствами привязки данных? - PullRequest
0 голосов
/ 28 марта 2019

Если я создаю пользовательский элемент управления с некоторыми элементами управления внутри него (у которого также есть некоторые привязки), как я могу удалить части привязки из пользовательского элемента управления 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 And 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 ]

1 голос
/ 29 марта 2019

И 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}" />
0 голосов
/ 29 марта 2019

Я нашел решение, хотя и не уверен, существует ли лучшее (я не нашел его через 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>

Отлично работает.

...