0

I'm making a custom control for a particular UI I am creating. Ideally, the user inputs a number into some textboxes, and upon pressing enter, those numbers get saved and can be used later in the program. I've narrowed down the program into the simplest terms to explain the problem. First I have two custom controls as follows:

    public class OuterCC : Control
    {
        
        public InnerCC CompA
        {
            get { return (InnerCC)GetValue(CompAProperty); }
            set { SetValue(CompAProperty, value); }
        }

        public static readonly DependencyProperty CompAProperty =
            DependencyProperty.Register("CompA", typeof(InnerCC), typeof(OuterCC), new PropertyMetadata(null));

        public InnerCC CompB
        {
            get { return (InnerCC)GetValue(CompBProperty); }
            set { SetValue(CompBProperty, value); }
        }

        public static readonly DependencyProperty CompBProperty =
            DependencyProperty.Register("CompB", typeof(InnerCC), typeof(OuterCC), new PropertyMetadata(null));

        public InnerCC CompC
        {
            get { return (InnerCC)GetValue(CompCProperty); }
            set { SetValue(CompCProperty, value); }
        }

        public static readonly DependencyProperty CompCProperty =
            DependencyProperty.Register("CompC", typeof(InnerCC), typeof(OuterCC), new PropertyMetadata(null));

        public InnerCC CompD
        {
            get { return (InnerCC)GetValue(CompDProperty); }
            set { SetValue(CompDProperty, value); }
        }

        public static readonly DependencyProperty CompDProperty =
            DependencyProperty.Register("CompD", typeof(InnerCC), typeof(OuterCC), new PropertyMetadata(null));

        public InnerCC CompE
        {
            get { return (InnerCC)GetValue(CompEProperty); }
            set { SetValue(CompEProperty, value); }
        }

        public static readonly DependencyProperty CompEProperty =
            DependencyProperty.Register("CompE", typeof(InnerCC), typeof(OuterCC), new PropertyMetadata(null));

        public InnerCC CompF
        {
            get { return (InnerCC)GetValue(CompFProperty); }
            set { SetValue(CompFProperty, value); }
        }

        public static readonly DependencyProperty CompFProperty =
            DependencyProperty.Register("CompF", typeof(InnerCC), typeof(OuterCC), new PropertyMetadata(null));

        public OuterCC()
        {
            CompA = new InnerCC();
            CompB = new InnerCC();
            CompC = new InnerCC();
            CompD = new InnerCC();
            CompE = new InnerCC();
            CompF = new InnerCC();
        }

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



        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            CompA = (InnerCC)GetTemplateChild("PART_compA");
            CompB = (InnerCC)GetTemplateChild("PART_compB");
            CompC = (InnerCC)GetTemplateChild("PART_compC");
            CompD = (InnerCC)GetTemplateChild("PART_compD");
            CompE = (InnerCC)GetTemplateChild("PART_compE");
            CompF = (InnerCC)GetTemplateChild("PART_compF");
            CompA.Index = 1;
            CompB.Index = 2;
            CompC.Index = 3;
            CompD.Index = 4;
            CompE.Index = 5;
            CompF.Index = 6;
        }
    }
    public class InnerCC : Control
    {

        public int Index
        {
            get { return (int)GetValue(IndexProperty); }
            set { SetValue(IndexProperty, value); }
        }

        public static readonly DependencyProperty IndexProperty =
            DependencyProperty.Register("Index", typeof(int), typeof(InnerCC), new PropertyMetadata(0));



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

    }

    public class ThicknessDivisionConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            Thickness thick = (Thickness)value;
            string str = (string)parameter;
            double divisor = Double.Parse(str);
            thick.Right /= divisor;
            thick.Left /= divisor;
            thick.Top /= divisor;
            thick.Bottom /= divisor;
            return thick;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

    public class DoubleDivisionConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            double dividend = (double)value;
            string str = (string)parameter;
            double divisor = Double.Parse(str);
            dividend /= divisor;
            return dividend;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

Here is my generic.xaml

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Testing"
    xmlns:cc="clr-namespace:Testing.CustomControls">


    <Style TargetType="{x:Type cc:OuterCC}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type cc:OuterCC}">
                    <Border BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}">
                        <StackPanel>
                            <cc:InnerCC x:Name="PART_compA"/>
                            <cc:InnerCC x:Name="PART_compB"/>
                            <cc:InnerCC x:Name="PART_compC"/>
                            <cc:InnerCC x:Name="PART_compD"/>
                            <cc:InnerCC x:Name="PART_compE"/>
                            <cc:InnerCC x:Name="PART_compF"/>
                        </StackPanel>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <Style TargetType="{x:Type cc:InnerCC}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type cc:InnerCC}">
                    <ControlTemplate.Resources>
                        <cc:ThicknessDivisionConverter x:Key="ThicknessDivisionConverter"/>
                        <cc:DoubleDivisionConverter x:Key="DoubleDivisionConverter"/>
                    </ControlTemplate.Resources>
                    <TextBox BorderBrush="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Border},Path=BorderBrush}"
                             BorderThickness="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Border},Path=BorderThickness,Converter={StaticResource ThicknessDivisionConverter},ConverterParameter=2.0}"
                             Height="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=StackPanel},Path=ActualHeight,Converter={StaticResource DoubleDivisionConverter},ConverterParameter=6.0}"/>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

My mainwindow.xaml is just as simple as possible to just test out different theories of why its not working

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Grid Grid.Column="0">
            <cc:OuterCC x:Name="TestInput" BorderBrush="Black" BorderThickness="5" />
        </Grid>
        <Grid Grid.Column="1">
            <TextBox x:Name="TestTextBox" Height="100"/>
        </Grid>

    </Grid>

And finally my mainwindow

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            TestInput.CompA.KeyDown += TestInput_KeyDown;
            TestTextBox.KeyDown += TestInput_KeyDown;
        }
        
        private void TestInput_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {

            }
        }
    }

If I set the debugger to pause the program when TestInput_KeyDown() is called, it will pause when I make a press any key in the normal textbox. However it is never called when I press a key in the custom control textbox. I've been troubleshooting this for over a day now and googling hasn't been able to get me anywhere. I'm a self-taught coder from youtube and google, is there something I am missing of why I can't get my custom control textboxes to activate key downs?

Other things I've tried: setting the focusable to true for innerCC, outerCC, and the main window, both separately and all together to see if that does anything. I've tried setting up RoutedEventHandlers in the custom controls. I've tried having InnerCC inherit from TextBox instead of Control.

Any help would be appreciated.

9
  • Why do you think that TestInput.CompA.KeyDown += TestInput_KeyDown; would attach an event handler to a TextBox? CompA is an InnerCC, not a TextBox. Commented Aug 9, 2024 at 20:25
  • @Clemens are you saying I need to Bind 'KeyDown' in the generic.xaml in order to cause the functionality to work? The reason I thought it would work is because Intellisense showed it pop up as an option in the Main Window c#, so I assumed it inherits the functionality from Control (or "TextBox" when I tested inheriting directly from there) Commented Aug 9, 2024 at 20:33
  • InnerCC is derived from Control, so it does also expose a KeyDown event. But InnerCC is not a TextBox, especially not the TextBox that you declare in the ControlTemplate. Commented Aug 9, 2024 at 20:36
  • @Clemens I think you are getting to the root of my ignorance. I tested trying to change the borderbrush programatically in the mainwindow.xaml.cs with by adding TestInput.CompA.BorderBrush = Brushes.Red; and it didn't change the color of the first textbox's border. Is there a reason that the OnApplyTemplate() function in the OuterCC class isn't connecting CompA to the declaration in the ControlTemplate? Again sorry for my ignorance, I'm trying to learn. Commented Aug 9, 2024 at 20:56
  • You seem to think that the TextBox in the ControlTemplate somehow replaces the InnerCC instance. That is not the case. The TextBox is a child element of InnerCC. Commented Aug 9, 2024 at 21:02

0

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.