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.
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 ?
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.
2 answers
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
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.