I wish to create a Custom Control, it should be a combination of predefined controls like Textbox, Button, ListBox, etc.,
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.
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. I added a Button to add the text in a List<string>
. To display the List I added a ListBox.
I need a Single control, it should inherit these three controls. In that I need an ItemsSource
for two way binding . If the List<string>
is updated, it should update the ItemSource.
I'm using this structure in more than 15 places. 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.
I've made you a minimal example of that desired 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
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. Rather use an ObservableCollection
since it notifies the View automaticly and you dont have to deal with that 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:
<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:
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.
Hope it helps.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.