简体   繁体   English

使用WPF C#中多个控件的组合创建自定义控件

[英]Create a Custom Control with the combination of multiple controls in WPF C#

I wish to create a Custom Control, it should be a combination of predefined controls like Textbox, Button, ListBox, etc., 我想创建一个自定义控件,它应该是预定义控件的组合,如Textbox,Button,ListBox等,

Kindly refer the following Controls (Just a Sample) 请参考以下控件(只是一个样本)

<Grid.RowDefinitions>
    <RowDefinition Height="30" />
    <RowDefinition Height="50" />
</Grid.RowDefinitions>
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="300" />
        <ColumnDefinition Width="100" />
    </Grid.ColumnDefinitions>
    <TextBox Grid.Column="0" />
    <Button Grid.Column="1" Content="Add" Margin="20,0" />
</Grid>
<ListBox ItemsSource="{Binding textBox}" Grid.Row="1" Margin="0,25">
    <ListBoxItem />
</ListBox>
</Grid>

I need a combination of controls in a single custom control. 我需要在单个自定义控件中组合控件。 I need to add the Textbox values in a ListItem while I'm hitting the button and finally I need the List from this control. 当我按下按钮时,我需要在ListItem中添加Textbox值,最后我需要来自此控件的List。

Expected Custom Control (Just a Sample) 预期的定制控制(只是一个样本)

<cust:MultiControl ItemsSource="{Binding stringCollection}" />

Description : I need to get the list of string from the user. 描述 :我需要从用户那里获取字符串列表。 I added a TextBox to get the input from the User. 我添加了一个TextBox来获取用户的输入。 I added a Button to add the text in a List<string> . 我添加了一个Button来在List<string>添加文本。 To display the List I added a ListBox. 为了显示List,我添加了一个ListBox。

I need a Single control, it should inherit these three controls. 我需要一个Single控件,它应该继承这三个控件。 In that I need an ItemsSource for two way binding . 在那我需要一个ItemsSource 双向绑定 If the List<string> is updated, it should update the ItemSource. 如果List<string>已更新,则应更新ItemSource。

I'm using this structure in more than 15 places. 我在超过15个地方使用这种结构。 So, I wish to make it as Custom control. 所以,我希望将其作为自定义控件。 Kindly assist me how to implement this as a single control ? 请帮助我如何将其作为单一控件实现?

在此输入图像描述

I don't need a User Control, I need a Custom Control kindly assist me... 我不需要用户控件,我需要一个自定义控件,请帮助我......

Item Source ViewModel Collection is not updating even-though the ItemsSource has value. 项目源ViewModel集合甚至不更新 - 尽管ItemsSource具有值。

在此输入图像描述

I've made you a minimal example of that desired CustomControl. 我已经为您提供了所需CustomControl的最小示例。

The Control 控制

public class MyCustomControl : ItemsControl {

        static MyCustomControl() {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCustomControl), new FrameworkPropertyMetadata(typeof(MyCustomControl)));
        }

        private Button _addButton;
        private TextBox _textBox;
        private ListView _itemsView;

        public override void OnApplyTemplate() {
            this._addButton = this.GetTemplateChild("PART_AddButton") as Button;
            this._textBox = this.GetTemplateChild("PART_TextBox") as TextBox;
            this._itemsView = this.GetTemplateChild("PART_ListBox") as ListView;

            this._addButton.Click += (sender, args) => {
                (this.ItemsSource as IList<string>).Add(this._textBox.Text);
            };
            this._itemsView.ItemsSource = this.ItemsSource;
            base.OnApplyTemplate();
        }

        public ICommand DeleteCommand => new RelayCommand(x => { (this.ItemsSource as IList<string>).Remove((string)x); });
    }

The Template 模板

 <Style TargetType="{x:Type local:MyCustomControl}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:MyCustomControl}">
                <Border Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto" />
                            <RowDefinition Height="Auto" />
                        </Grid.RowDefinitions>
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="300" />
                                <ColumnDefinition Width="100" />
                            </Grid.ColumnDefinitions>
                            <TextBox x:Name="PART_TextBox" Grid.Column="0" />
                            <Button x:Name="PART_AddButton" Grid.Column="1" Content="Add" Margin="20,0" />
                        </Grid>
                        <ListView ItemsSource="{TemplateBinding ItemsSource}" Grid.Row="1" Margin="0,25" x:Name="PART_ListBox" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" >
                            <ListView.ItemTemplate>
                                <DataTemplate>
                                    <StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch">
                                        <TextBlock Text="{Binding}"/>
                                        <Button Content="X" Foreground="Red" 
                                                Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MyCustomControl}}, Path=DeleteCommand}" 
                                                CommandParameter="{Binding}" Margin="10,0,0,0"></Button>
                                    </StackPanel>
                                </DataTemplate>
                            </ListView.ItemTemplate>
                        </ListView>
                    </Grid>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

RelayCommand RelayCommand

public class RelayCommand : ICommand
    {
        #region Fields

        private readonly Action<object> _execute;
        private readonly Predicate<object> _canExecute;

        #endregion // Fields

        #region Constructors

        public RelayCommand(Action<object> execute, Predicate<object> canExecute = null)
        {
            if (execute == null)
                throw new ArgumentNullException(nameof(execute));

            this._execute = execute;
            this._canExecute = canExecute;
        }

        #endregion // Constructors

        #region ICommand Members

        [DebuggerStepThrough]
        public bool CanExecute(object parameter)
        {
            return this._canExecute == null || this._canExecute(parameter);
        }

        public event EventHandler CanExecuteChanged {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        public void Execute(object parameter)
        {
            this._execute(parameter);
        }

        #endregion // ICommand Members
    }

Usage 用法

 <local:MyCustomControl ItemsSource="{Binding Collection}"/>

Note Do not use a List as your ItemsSource. 注意请勿使用List作为ItemsSource。 Rather use an ObservableCollection since it notifies the View automaticly and you dont have to deal with that Update-Stuff 而是使用ObservableCollection因为它自动通知View,你不必处理那个Update-Stuff

Cheers 干杯

Suppose this is your custom control: 假设这是您的自定义控件:

<UserControl x:Class="CustomControl.UserControl1"
         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" 
         xmlns:local="clr-namespace:CustomControl"
         mc:Ignorable="d" >
<StackPanel Width="200" Margin="15">
    <TextBox  Name="txtbox"/>
    <Button  Content="Add"
            Margin="20,0"  Click="Button_Click"/>

    <ListBox ItemsSource="{Binding}"
             Margin="0,25">
    </ListBox>

</StackPanel>

And this is your Parentwindow Calling your custom control: 这是您的Parentwindow调用您的自定义控件:

<Window x:Class="ParentControl.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:ParentControl"
    mc:Ignorable="d"
    xmlns:customcontrol="clr-namespace:CustomControl;assembly=CustomControl"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <customcontrol:UserControl1 Name="customcontrol"></customcontrol:UserControl1>
</Grid>

you have a string collection which you want to be updated with the text in the textbox, you can do something like this: In the parent window set the DataContext of the custom control to the string collection, like this: 你有一个字符串集合,你想用文本框中的文本更新,你可以这样做:在父窗口中将自定义控件的DataContext设置为字符串集合,如下所示:

        public MainWindow()
    {
        InitializeComponent();

        ObservableCollection<string> stringcollection = new ObservableCollection<string>();
        stringcollection.Add("String 1");
        stringcollection.Add("String 2");
        stringcollection.Add("String 2");
        stringcollection.Add("String 3");
        customcontrol.DataContext = stringcollection;

    }

and in your custom control back logic add handler to the button click event and do something like this: 并在您的自定义控件后退逻辑中添加处理程序到按钮单击事件并执行以下操作:

 private void Button_Click(object sender, RoutedEventArgs e)
    {
        var button = sender as Button;
        var list = button.DataContext as ObservableCollection<string>;
        list.Add(this.txtbox.Text.ToString());
    }

make sure that the string collection is of Type Observable Collection, otherwise you listbox wont get updated everytime you click the add button. 确保字符串集合是Type Observable Collection,否则每次单击“添加”按钮时列表框都不会更新。

Hope it helps. 希望能帮助到你。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM