wotnot
wotnot

Reputation: 281

Binding to elements within a UserControl

C#, WPF.

I am trying to implement a UserControl with elements bound to properties in an object hierarchy. I have been using this as a reference.

I have created the following minimal example. It implements three instances of the UserControl, with the textbox in each case representing a filename. A dependency property is used to permit binding. Although it executes without errors, the textboxes are blank. They should contain "test1", "test2" and "test3". What am I missing?

Main window:

<Window x:Class="CustomControlTest.MainWindow"
          xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
          xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
          xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
          xmlns:mycontrols="clr-namespace:MyControls"
          mc:Ignorable="d"
          Title="MainWindow" Height="100" Width="800">
      <Grid>
          <StackPanel Orientation="Vertical" DataContext="{Binding ElementName=parent}">
              <mycontrols:DataFileControl x:Name="Ctrl1" FName="{Binding Path=project.File1.Filename}"/>
              <mycontrols:DataFileControl x:Name="Ctrl2" FName="{Binding Path=project.File2.Filename}"/>
              <mycontrols:DataFileControl x:Name="Ctrl3" FName="{Binding Path=project.File3.Filename}"/>
          </StackPanel>
      </Grid>
</Window>

namespace CustomControlTest
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        Project project = new Project();
        public MainWindow()
        {
            InitializeComponent();
            project.File1.Filename = "test1";
            project.File2.Filename = "test2";
            project.File3.Filename = "test3";
        }
        
    }

    public class Project : INotifyPropertyChanged
    {
        public DataFile File1 { get; set; } = new DataFile();
        public DataFile File2 { get; set; } = new DataFile();
        public DataFile File3 { get; set; } = new DataFile();
    
        public event PropertyChangedEventHandler PropertyChanged;
    }

    public class DataFile : INotifyPropertyChanged
    {
        public string Filename { get; set; } = "";
    
        public event PropertyChangedEventHandler PropertyChanged;
    }  
}

UserControl:

<UserControl x:Class="MyControls.DataFileControl"
             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="40"
             d:DesignWidth="800"
             Name="TheCtrl">
    <Grid Height="20">
        <TextBox Name="filenameTextBox" Margin="5,0,5,0" Text="{Binding ElementName=TheCtrl, Path=FName, Mode=TwoWay}"/>
    </Grid>
</UserControl>

namespace MyControls
{
    public partial class DataFileControl : System.Windows.Controls.UserControl
    {

        public string FName
        {
            get { return (string)GetValue(FNameProperty); }
            set { SetValue(FNameProperty, value); }
        }

        public static readonly DependencyProperty FNameProperty =
            DependencyProperty.Register("FName", typeof(string), typeof(DataFileControl), new PropertyMetadata(null));


        public DataFileControl()
        {
            InitializeComponent();
        }

    }
}

Upvotes: 0

Views: 74

Answers (1)

Clemens
Clemens

Reputation: 128013

The expression

FName="{Binding Path=project.File1.Filename}"

requires a public property named project in the current DataContext, which you have not set. You should have noticed a data binding error message in the Output Window in Visual Studio when you debug the application.

Change it to

public Project project { get; } = new Project();

public MainWindow()
{
    InitializeComponent();
    project.File1.Filename = "test1";
    project.File2.Filename = "test2";
    project.File3.Filename = "test3";
    DataContext = this;
}

Alternatively, use the Project instance as DataContext (and thus make it a view model)

private readonly Project project = new Project();

public MainWindow()
{
    InitializeComponent();
    project.File1.Filename = "test1";
    project.File2.Filename = "test2";
    project.File3.Filename = "test3";
    DataContext = project;
}

and change the binding expressions to

FName="{Binding File1.Filename}"

Upvotes: 1

Related Questions