Criar um controle personalizado com a combinação de vários controles em WPF C#

Eu desejo criar um controle personalizado, que deve ser uma combinação de controles predefinidos como Textbox, Button, ListBox, etc.,

Por favor, consulte os seguintes controlos (apenas uma amostra)

<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>
Preciso de uma combinação de comandos num único controlo personalizado. Preciso de adicionar os valores da caixa de texto num ListItem enquanto carrego no botão e finalmente preciso da lista deste controlo.

controlo personalizado esperado (apenas uma amostra)

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

Descrição : I precisa obter a lista de string do Usuário. Eu adicionei uma caixa de texto para obter a entrada do Usuário. Adicionei um botão para adicionar o texto em um List<string>. Para mostrar a lista, adicionei uma lista.

Preciso de um único controlo, deve herdar estes três controlos. Na medida em que eu preciso de um ItemsSource para ligação de duas vias. Se o List<string> for actualizado, deverá actualizar o ItemSource.

Estou a usar esta estrutura em mais de 15 lugares. Por isso, quero torná-lo um controlo personalizado. Por favor, ajude-me a implementar isto como um único controle ?

enter image description here

Não preciso de um controlo de utilizador, preciso de um controlo personalizado, por favor, ajude-me...

a colecção de itens Source ViewModel não está a ser actualizada, embora o ItemsSource tenha valor.

enter image description here

Author: B.Balamanigandan, 2016-08-09

2 answers

Fiz de ti um exemplo mínimo desse controlo personalizado desejado.

O Controlo

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); });
    }

O Modelo

 <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

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
    }

Utilização

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

Nota Não utilize um List como sua fonte ItemsSource. Em vez disso, use um ObservableCollection uma vez que notifica automaticamente a vista e você não tem que lidar com esse Update-Stuff

Saúde.
 5
Author: lokusking, 2016-08-12 09:32:11

Suponha que este é o seu controlo personalizado:

<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>

E esta é a tua janela familiar a chamar o teu controlo personalizado.
<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>

Você tem uma colecção de texto que deseja actualizar com o texto da caixa de texto, você pode fazer algo como isto: Na janela-mãe, defina o texto de dados do controlo personalizado para a colecção de Textos, do seguinte modo:

        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;

    }

E na sua lógica de controlo personalizado Adicione manipulador ao evento botão clique e faça alguma coisa assim:

 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());
    }

Certifique-se que a colecção de texto é do tipo de colecção observável, caso contrário a lista não será actualizada sempre que carregar no botão Adicionar.

Espero que ajude.
 2
Author: Sameed, 2016-08-09 05:55:50