3

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 ?

enter image description here

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.

enter image description here

4
  • A CustomControl is just a class that derives from Control. UserControl is also a class that derives from Control (indirectly), so a UserControl is a CustomControl. What's cool with UserControl is you can define them easily with a XAML file + code while a CustomControl is just code. So you really want a UserControl. You just need to add custom code to it, like DependencyProperties (for your exposed ItemSource) and custom logic. Commented Aug 11, 2016 at 6:04
  • Do you basically need an example of a working custom control? How to build one from scratch? Commented Aug 11, 2016 at 13:05
  • Please tell us, why dont you simply make a CustomControl? Whats your problem? If you know how to write a UserControl, there should be no problem writing a CustomControl... I Really dont get what you are asking for Commented Aug 11, 2016 at 14:43
  • @lokusking I need a control with structure as I mentioned in the question. In that I need a ItemSource attribute to bind the List<string>. The Control should take of all the functionality as I mentioned in the Question. You may have solution for my requirement in User-control means, then give your answer. Commented Aug 11, 2016 at 16:48

2 Answers 2

15
+50

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

Sign up to request clarification or add additional context in comments.

11 Comments

Thanks a lot... This is amazing, this is my actual expectation. Thanks for your guidance.
Thanks. I need an additional option to remove the Item from the List.
@IRPunch Updated control with deleteButton
@lokusking - Thanks. But I need a delete Button within <ListView.ItemTemplate> each particular item I need a delete button to delete the Item.
@lokusking - I tried a Autocomplete Textbox with the same logic. I added one more dependency property IEnumerable<dynamiic> SuggestionItemSource. I binded the property in listbox. If I bind a ObservableCollection<string>, then it works perfectly, If i use ObservableCollection <int>, then it not works.
|
2

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.

3 Comments

I'm expecting the solution in Custom Control not by UserControl. Thanks for your kind reply...
No. Its not a Custom Control. Just refer the Screenshot I apex-ed in the question block.
I need this as an independent control. If I implement user control means, its dependent on the project level (i.e., parent window level).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.