0

Hi and thanks for your attention. (I apologize for editing the question so much and forgetting to properly proof read it first)

I am currently trying to develop a new WPF custom control that contains a small Plus button inside a text field for adding new items, to replace separate button and textboxes in the UI. Sounds simple enough but no matter how much I try I seem to not get the Data Bindings right.

The application is supposed to work with user entering a name in the textbox of my user control, and then the button of user control fires the AddCommand when button binded to add command inside my custom control is pressed then method attached to AddCommand handle the rest.

Generally I don't get an error and the problem is that due to some codes in other parts of application if the "textbox" I had binded to a Field(here for instance is: NewPsCondition.Name) is empty(or in this case not properly binded) the application won't allow me to actually fire AddCommand(by deactivating the button 'correctly' binded to AddCommand). However I always get an error when I try to fire AddCommand from my own custom control's button and if I try to set NewPsCondition.Name in textbox of my own control and fire the AddCommand on another button, that said button will be de-active. Further suggesting that I am not doing the binding right.

So far I tried all the other answers I found on Stackoverflow and google such as:

  • adding this.DataContext = this; to custom user control constructor.
  • adding RootElement.DataContext = this to custom user control constructor.
  • making sure to have "Mode=TwoWay" where I call my user control.
  • making sure the problem is actually with the custom user control by testing everything as it is but with separate button and textbox as was before.

this is an example of one of the places that I should call my control and how I am calling it now:

<StackPanel Orientation="Horizontal" >
                    <Label Content="نام"></Label>
<!--These bindings don't work-->
                    <control:AddFieldMV Margin="35px 0" Width="132px" SubmitCommand="{Binding Path=AddCommand}" InputText="{Binding Path=NewPsCondition.Name,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
<!--These bindings do-->                    
                    <Button  Content="اضافه" Command="{Binding Path=AddCommand}" Click="ButtonBase_OnClick"></Button>
                    <TextBox Text="{Binding Path=NewPsCondition.Name,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}" Width="60px" Margin="40px 0"></TextBox>
</StackPanel>

This is the xaml code of my custom control:

<UserControl x:Class="TestInstrumentApplication.ResourceDictionary.Control.AddFieldMV"
             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:TestInstrumentApplication.ResourceDictionary.Control"
             mc:Ignorable="d" 
             DataContext="this"
             MinHeight="20px" MinWidth="70px">
    <Grid x:Name="RootElement">
        <Border
            BorderBrush="#6D6E71"
            BorderThickness="0 0 0 1"
            CornerRadius="0">
            <Grid MinHeight="20" Background="#F1F2F2">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*"></ColumnDefinition>
                    <ColumnDefinition  Width="15"></ColumnDefinition>
                </Grid.ColumnDefinitions>
                <TextBox MinWidth="50" Grid.Column="0" HorizontalContentAlignment="Center" VerticalAlignment="Center" VerticalContentAlignment="Center" BorderThickness="0" Background="Transparent" Text="{Binding Path=InputText}">
                </TextBox>
                <Button Grid.Column="1" Height="20px" BorderBrush="Transparent" Click="AddButton_OnClick">
                    <Button.Background>
                        <VisualBrush  >
                            <VisualBrush.Visual>
                                <Canvas Width="14.250" Height="14.250">
                                    <!--there were some working vector graphics here that I removed to keep it short-->                                </Canvas>
                            </VisualBrush.Visual>
                        </VisualBrush>
                    </Button.Background>
                </Button>
            </Grid>
        </Border>
    </Grid>
</UserControl>

and finally these are the custom control's C# code behind xaml code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using TestInstrumentApplication.Utility;

namespace TestInstrumentApplication.ResourceDictionary.Control
{
    /// <summary>
    /// Interaction logic for AddFieldMV.xaml
    /// </summary>
    public partial class AddFieldMV : UserControl
    {
        public AddFieldMV()
        {
            InitializeComponent();
            RootElement.DataContext = this;
        }

        public event EventHandler AddButtonClicked;

        protected virtual void OnAddButtonClicked(EventArgs e)
        {
            AddButtonClicked?.Invoke(this, e);
        }

        private void AddButton_OnClick(object sender, RoutedEventArgs e)
        {
            OnAddButtonClicked(e);
            //here I get a bug when I hit button on running application telling me SubmitCommand is null
            SubmitCommand.Execute(sender);
        }

        public static readonly DependencyProperty SubmitCommandProperty
            = DependencyProperty.Register(
                "SubmitCommand",
                typeof(ICommand),
                typeof(AddFieldMV));

        public ICommand SubmitCommand
        {
            get => (ICommand)GetValue(SubmitCommandProperty);
            set => SetValue(SubmitCommandProperty, value);
        }

        public static readonly DependencyProperty InputTextProperty
            = DependencyProperty.Register(
                "InputText",
                typeof(string),
                typeof(AddFieldMV),
                new PropertyMetadata(default(string)));

        public string InputText
        {
            get
            {
                return (string)GetValue(InputTextProperty);
            }
            set { SetValue(InputTextProperty, value); }
        }

        private static void CustomTextBox_OnTextPropertyChanged(DependencyObject d,
            DependencyPropertyChangedEventArgs e)
        {
            AddFieldMV customTextBox = d as AddFieldMV;

            customTextBox.SetValue(InputTextProperty, e.NewValue);
        }
    }
}
Soorena Aban
  • 710
  • 2
  • 10
  • 18
  • 1
    As a note, while you may have found `this.DataContext = this;` as advice on StackOverflow, it is totally wrong. You must never explicitly set the DataContext of a UserControl where you also expose dependency properties. If you set the DataContext, the usual DataContext-based Bindings of the properties of your control won't work anymore (like `SubmitCommand="{Binding Path=AddCommand}"`). Also, `DataContext="this"` in the UserControl's XAML is nonsense. It just assigns the string "this" to the DataContext. – Clemens Jan 12 '19 at 08:12
  • 1
    The Bindings in the UserControl's XAML should then use a RelativeSource (instead of the default DataContext): `Text="{Binding Path=InputText, RelativeSource={RelativeSource AncestorType=UserControl}}"`. – Clemens Jan 12 '19 at 08:16
  • @Clemens You are correct, I found 2 articles that pointed out the same mistake just as you did when binding for user controls with explanations. sadly however, neither the "this.DataContext = this" or the alternative solutions mentioned in those articles have worked for me. – Soorena Aban Jan 12 '19 at 08:20
  • 1
    If you remove all assignments to the control's DataContext, including the one in XAML, `SubmitCommand="{Binding Path=AddCommand}"` should properly pass the value of the view model's AddCommand property to your SubmitCommand property. – Clemens Jan 12 '19 at 08:29
  • @Clemens thanks to you my problem is solved! as you probably noticed yourself, I actually made 3 different errors all at once, and that I had to solve all 3 of them, but when corrected just 1 of them and problem wouldn't have gotten away, I would have just hit Ctrl+Z like a noob and just got back to square one. reading my writing and code must have not been easy for you (I haven't slept for 30 hours and haven't coded in WPF for a long time) so I truly appreciate the time and attention you spent helping me here. – Soorena Aban Jan 12 '19 at 08:50

0 Answers0