FlyingFoX
FlyingFoX

Reputation: 3509

How to properly set up a WPF UserControl for Binding

I want to create a UserControl, that essentially is a Label with a TextBox. Now I want to be able to Bind TextBox.Text to different values.

For this I created a DependencyProperty in my UserControl and am now trying to bind something to that newly created DependencyProperty, but the Text seems not to get updated.

My UserControl1.xaml looks like this:

<UserControl x:Class="WpfApplication1.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" 
         mc:Ignorable="d" 
         d:DesignHeight="48" d:DesignWidth="200">
<Grid>
    <WrapPanel Height="48" HorizontalAlignment="Left" Name="wrapPanel1" VerticalAlignment="Top" Width="200">
        <Label Content="Label" Height="48" Name="label1" Width="100" />
        <TextBox Height="48" Name="textBox1" Width="100"  />
    </WrapPanel>
</Grid>

And my UserControl1.xaml.cs looks like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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 System.Diagnostics;

namespace WpfApplication1
{


    /// <summary>
    /// Interaction logic for UserControl1.xaml
    /// </summary>
    public partial class UserControl1 : UserControl
    {
        private string value;
        public string Value
        {
            get { return value; }
            set
            {
                this.value = value;
                textBox1.Text = value;
                Trace.TraceInformation("value set in UserControl1");
            }
        }
        public static readonly DependencyProperty ValueProperty =
            DependencyProperty.Register("Value", typeof(string), typeof(UserControl1));
        public UserControl1()
        {
            InitializeComponent();
        }
    }
}

I am using the UserControl like this:

<my:UserControl1 x:Name="userControl11" Value="{Binding Path=Name}" />

with DataContext set to an object that has a Name property and implements INotifyPropertyChanged for this property.

Upvotes: 1

Views: 1487

Answers (3)

Tyler Morrow
Tyler Morrow

Reputation: 961

Take a look at this implementation. It uses a very simple MVVM design to obtain databinding. Basically this is what it's doing:

  • You have your UserControl (the View) that sets it's DataContext to a corresponding ViewModel.
  • The TextBox on the View is then bound to a public property in that ViewModel
  • By implementing INotifyPropertyChanged, you are enabling your ViewModel to effectively update the UI anytime it changes the value of the Text property.

This can repeated for any number of bindings and applied to many different types such as Lists, ObservableCollections, integers.

View

<UserControl x:Class="WpfApplication1.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" 
         mc:Ignorable="d" d:DesignHeight="48" d:DesignWidth="200">
<Grid>
    <WrapPanel Height="48" HorizontalAlignment="Left" Name="wrapPanel1" VerticalAlignment="Top" Width="200">
        <Label Content="Label" Height="48" Name="label1" Width="100" />
        <TextBox Height="48" Name="textBox1" Width="100" Text={Binding Text} />
    </WrapPanel>
</Grid>

View Code-Behind

namespace WpfApplication1
{
    using System.Windows;
    using System.Windows.Controls;

    public partial class UserControl1: UserControl
    {
        public UserControl1()
        {
            DataContext = new UserControl1ViewModel();
            InitializeComponent();
        }
    }
}

ViewModel

namespace WpfApplication1
{
    using System.Collections.ObjectModel;
    using System.Windows.Controls;

    class UserControl1ViewModel : INotifyPropertyChanged
    {
        // Ultimately, this field (text) would be moved to a model along with 
        // any other data fields for this view but for the sake of space, 
        // I've put it in the ViewModel.
        private string text = "";
        public string Text 
        { 
            get { return text; } 
            set 
            { 
                text = value;
                RaisePropertyChanged("Text");
            } 
        }

        public MainWindowViewModel()
        {
            Text = "Hello!";
        }

        // This is the implementation of INotifyPropertyChanged that is used 
        // to inform the UI of changes.
        public event PropertyChangedEventHandler PropertyChanged;
        protected void RaisePropertyChanged(string propertyName)
        {
            var handler = PropertyChanged;
            if (handler != null)
                handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Good luck!

Upvotes: 2

Alan
Alan

Reputation: 7951

You cannot add additional logic or code to the get or set accessor for a property that wraps a dependency property. It will not be executed.

The reason for this, is because the WPF designer will actually generate code to use the DependencyProperty directly. The get/set property is there just for convenience if you use it in the code. Because you want the DependencyProperty and the property's get/set to do the same thing, you should only call GetValue and SetValue in the get/set accessors while passing in the related dependency property.

See these tutorials:

Dependency Properties Dependency Properties Overview

Upvotes: 2

Bill Zhang
Bill Zhang

Reputation: 1929

You put connection between TextBox's Text and UserControl's Value in wrong place. The CLR property is used for convenience but it is not used by Bind Engine. You need bind TextBox's Text to Usercontrol's Value explicitly on XAML or code behind, such as (assuming you give your user control a name called root):

<TextBox x:Name="textBox1" Text="{Binding Path=Value, ElementName=root}"/>

Upvotes: 2

Related Questions