tags:

views:

732

answers:

2

I am using UserControl which has TextBox, Button control. The Button opens a FileDialog and user selects the file. The selected file is transferred into the FileName property which is a dependency property. For some reason the TextBox is not binding to this property. Here is the code:

<UserControl x:Class="WPF3D.FileInputBox"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Height="300" Width="300" x:Name="root">
    <Grid>

        <StackPanel>            
        <TextBox Name="txtFile" Text="{Binding FileName, ElementName=root}" Width="300" Height="20" />
        <Button Content="Select File" Width="100" Height="20" Click="SelectFile"  /> 
        </StackPanel>



    </Grid>
</UserControl>

And here is the code for the UserControl.cs file:

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 Microsoft.Win32;
using System.Windows.Markup;

namespace WPF3D
{
    [ContentProperty("FileName")]
    public partial class FileInputBox : UserControl
    {
        public static readonly DependencyProperty FileNameProperty = DependencyProperty.Register("FileName",
                                                                                                 typeof (String),
                                                                                                 typeof (FileInputBox));


        public event EventHandler<EventArgs> FileNameChanged; 

        public FileInputBox()
        {
            InitializeComponent();
            txtFile.TextChanged += new TextChangedEventHandler(txtFile_TextChanged);
        }

        void txtFile_TextChanged(object sender, TextChangedEventArgs e)
        {
            e.Handled = true; 
            if(FileNameChanged != null)
            {
                FileNameChanged(this, EventArgs.Empty); 
            }

        }

        public string FileName
        {
            get { return (string) GetValue(FileNameProperty); }
            set { SetValue(FileNameProperty,value);}
        }


        private void SelectFile(object sender, RoutedEventArgs e)
        {
            // select the file 
            var fileDialog = new OpenFileDialog();
            fileDialog.ShowDialog();

            this.FileName = fileDialog.FileName;
        }

        protected override void OnContentChanged(object oldContent, object newContent)
        {
            if(oldContent != null)
                throw new InvalidOperationException("You can't change the content"); 
        }
    }
}
A: 

Got the answer using the following link:

http://geekswithblogs.net/thibbard/archive/2008/04/22/wpf-custom-control-dependency-property-gotcha.aspx

It seems this is a lame limitation of the WPF framework!

azamsharp
+3  A: 

I think this is just a scoping issue as evident by this output in the debug window:

System.Windows.Data Error: 4 : Cannot find source for binding with reference 'ElementName=root'. BindingExpression:Path=FileName; DataItem=null; target element is 'TextBox' (Name='txtFile'); target property is 'Text' (type 'String')

If you just change it to this, it works fine:

<UserControl x:Class="TestApp.FileInputBox"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Height="300" Width="300">
    <Grid x:Name="_grid">
     <StackPanel>
      <TextBox Name="txtFile" Text="{Binding FileName}" Width="300" Height="20" />
      <Button Content="Select File" Width="100" Height="20" Click="SelectFile"  />
     </StackPanel>
    </Grid>
</UserControl>

And the important part of the code-behind:

public FileInputBox()
{
    InitializeComponent();
    txtFile.TextChanged += new TextChangedEventHandler(txtFile_TextChanged);
    _grid.DataContext = this;
}

Note that setting the DataContext on the Grid rather than the UserControl is intentional. If you do it at the UserControl level it becomes possible for consumers of your control to break your bindings simply by changing the DataContext of your UserControl.

HTH, Kent

Kent Boogaart