0

Apologies if someone can find an already answered question that works for this, I've been digging around for 4 hours and I don't know if my search criteria is wrong, or if I'm going insane.

I am working on an MVVM project with custom controls. My custom controls will pull in whatever is already in the ViewModel when it loads, but it will not let me update the value via the interface. I've tried a couple different solutions and at this point my head is spinning. Below is my code.

If you need the code for my view model, please let me know. It's just a basic getter and setter so I only included the relevant code for the control and its usage.

TitledTextBox.cs:

public class TitledTextBox : Control
    {
        public static DependencyProperty TextLabelProperty = DependencyProperty.Register("TextLabel", typeof(string), typeof(TitledTextBox));
        public static DependencyProperty TextValueProperty = DependencyProperty.Register("TextValue", typeof(string), typeof(TitledTextBox));

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

        public string TextLabel
        {
            get { return (string)GetValue(TextLabelProperty); }
            set { SetValue(TextLabelProperty, value); }
        }

        public string TextValue
        {
            get { return (string)GetValue(TextValueProperty); }
            set { SetValue(TextValueProperty, value); }
        }
    }

Generic.xaml:

<Style TargetType="{x:Type local:TitledTextBox}" >
        <Setter Property="Visibility" Value="Visible"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:TitledTextBox}">
                    <Grid Visibility="{TemplateBinding Visibility}">
                        <Rectangle Fill="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"  />
                        <TextBox Text="{TemplateBinding TextValue}" FontSize="11" BorderThickness="0" Margin="2,10,2,2" Background="Transparent" TextWrapping="Wrap" />
                        <Label Content="{TemplateBinding TextLabel}" FontWeight="Bold" FontSize="8.0" Foreground="Black" VerticalAlignment="Top" Margin="0,-5,0,2"/>
                        <Border BorderBrush="Gray" BorderThickness="0.8,0.8,0,0" Margin="0" CornerRadius="3"/>
                        <Border BorderBrush="DimGray" BorderThickness="0,0,0.8,0.8" Margin="0" CornerRadius="3" >
                            <Border.Effect>
                                <DropShadowEffect ShadowDepth="1" BlurRadius="5" Color="DimGray"/>
                            </Border.Effect>
                        </Border>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

And the usage in my TicketWindow.xaml:

xmlns:ctrl="clr-namespace:PIUS.MOPOCommunicationTool.Controls"
<ctrl:TitledTextBox Grid.Row="1" Grid.Column="2" Margin="2" TextLabel="CUSTOMER" TextValue="{Binding Ticket.Customer, UpdateSourceTrigger=PropertyChanged}"/>

If I check the Live Property Explorer, I can see that the binding on the TextBox in the custom control is evaluating correctly, but the TextValue of the TitledTextBox and the property in my ViewModel are not updating.

I've tried doing a PropertyChangedCallback like below, but that didn't work. It fires when the window loads in, but it doesn't fire when I change the value in the UI.

public static DependencyProperty TextValueProperty = DependencyProperty.Register("TextValue", typeof(string), typeof(TitledTextBox)
            , new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(TitledTextBox_OnTextPropertyChanged)));

private static void TitledTextBox_OnTextPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            TitledTextBox box = sender as TitledTextBox;
            box.SetValue(TextValueProperty, e.NewValue);
        }

I've tried fiddling with the TemplateBinding v. Binding and got nowhere with that either. I feel like I'm missing something incredibly simple here.

Xaraphena
  • 71
  • 1
  • 9
  • 1
    The problem is that `TemplateBinding` is an optimized form of binding and it is limited to one-way binding. You need to change that `TemplateBinding` for the TextBox to instead use a regular binding with `TemplatedParent`. That will allow your two-way binding to actually work. This answer explains it https://stackoverflow.com/a/5913305/5869304 – Joe Apr 13 '22 at 21:55
  • Thank you so much! If I could give you a cookie, I would. Please post this as the answer so I can give you the check mark! – Xaraphena Apr 13 '22 at 21:59
  • 1
    You are welcome. I would post it as an answer but since the question I linked to has already answered it, it would be a duplicate answer and sort of against the spirit of answering things twice here. Stuff like this used to trip me up a lot. Slowly you get really good at understanding how to debug stuff like this when it happens. – Joe Apr 13 '22 at 22:02
  • Don't forget to configure the `TextValue` property to bind `TwoWay`. Additionally, don't bind a string to a `Label.Content`. The performance is really not good. Better replace the `Label` with a `TextBlock`. – BionicCode Apr 13 '22 at 22:04
  • 1
    He set the property up with `BindsTwoWayByDefault` – Joe Apr 13 '22 at 22:22

0 Answers0