Reputation: 99
I made a super easy user control to browse files
<UserControl x:Class="DrumMapConverter.FileBrowserTextBox"
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"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
mc:Ignorable="d" Height="24" d:DesignWidth="500">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Name="lblLabel" Text="{Binding Label, Mode=TwoWay}" MinWidth="150"/>
<Button Grid.Column="1" Content=" ... " Click="BrowseButton_Click"/>
<TextBox Grid.Column="2" Name="txtFilepath" Text="{Binding FilePath, Mode=TwoWay}"/>
</Grid>
</UserControl>
With 2 Dependency properties :
Label and Filepath :
// FilePath
public static readonly DependencyProperty FilePathProperty =
DependencyProperty.Register("FilePath", typeof(string), typeof(FileBrowserTextBox), new UIPropertyMetadata(string.Empty, new PropertyChangedCallback(OnFilePathPropertyChanged)));
public string FilePath
{
get { return (string)GetValue(FilePathProperty); }
set { SetValue(FilePathProperty, value); }
}
static void OnFilePathPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
var obj = o as FileBrowserTextBox;
if (obj == null)
return;
FileBrowserTextBox fileBrowserTextBox = (FileBrowserTextBox)o;
fileBrowserTextBox.txtFilepath.Text = (string)e.NewValue;
}
// Label
public static readonly DependencyProperty LabelProperty =
DependencyProperty.Register("Label", typeof(string), typeof(FileBrowserTextBox), new UIPropertyMetadata(string.Empty, new PropertyChangedCallback(OnLabelPropertyChanged)));
public string Label
{
get { return (string)GetValue(LabelProperty); }
set { SetValue(LabelProperty, value); }
}
static void OnLabelPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
var obj = o as FileBrowserTextBox;
if (obj == null)
return;
FileBrowserTextBox fileBrowserTextBox = (FileBrowserTextBox)o;
fileBrowserTextBox.lblLabel.Text = (string)e.NewValue;
}
Then in my MainWindow ctor I have this
private DrumMapConverterDataModel model;
public MainWindow()
{
InitializeComponent();
model = new DrumMapConverterDataModel();
DataContext = model;
}
The Model has 2 Properties :
private string inputFile = "";
public string InputFile
{
get { return inputFile; }
set {
inputFile = value;
OnPropertyChanged("InputFile");
}
}
private string outputFile = "";
public string OutputFile
{
get { return outputFile; }
set
{
outputFile = value;
OnPropertyChanged("OutputFile");
}
}
That i bind in MainWindow.XAML like this
<cust:FileBrowserTextBox Label="Input File" FilePath="{Binding InputFile}"/>
<cust:FileBrowserTextBox Label="Output File" FilePath="{Binding OutputFile}"/>
run it and get this errors
System.Windows.Data Error: 40 : BindingExpression path error: 'InputFile' property not found on 'object' ''FileBrowserTextBox' (Name='')'. BindingExpression:Path=InputFile; DataItem='FileBrowserTextBox' (Name=''); target element is 'FileBrowserTextBox' (Name=''); target property is 'FilePath' (type 'String') System.Windows.Data Error: 40 : BindingExpression path error: 'OutputFile' property not found on 'object' ''FileBrowserTextBox' (Name='')'. BindingExpression:Path=OutputFile; DataItem='FileBrowserTextBox' (Name=''); target element is 'FileBrowserTextBox' (Name=''); target property is 'FilePath' (type 'String')
Which basically means there is no InputFile and OutputFile in the UserControl, but I'm trying to bind the FilePath property of the control with InputFile and OutputFile of my model, why isn't working ?
Thanks in advance.
Upvotes: 3
Views: 1992
Reputation: 33364
When you do
DataContext="{Binding RelativeSource={RelativeSource Self}}"
in FileBrowserTextBox
you overwrite inherited DataContext
changing binding context. Which means it will try to find InputFile
and OutputFile
properties inside FileBrowserTextBox
control. Remove that line and change binding within FileBrowserTextBox
so they don't affect DataContext
, for example using RelativeSource
, to something like:
<TextBlock Grid.Column="0" Name="lblLabel" Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Path=Label}" MinWidth="150"/>
<TextBox Grid.Column="2" Name="txtFilepath" Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Path=FilePath}"/>
also in PropertyChangedCallback
for Label
and FilePath
you do:
fileBrowserTextBox.txtFilepath.Text = (string)e.NewValue;
fileBrowserTextBox.lblLabel.Text = (string)e.NewValue;
If you only want to change UI you don't need to handle property changed callback at all. You've used bindings in XAML which should do that for you, taking binding context is fine, and lines above will only overwrite these bindings with fixed value
Upvotes: 3
Reputation: 81253
You have set DataContext
to itself on FileBrowserTextBox
which makes all default bindings to search for property in itself.
Avoid setting DataContext
for UserControl and bind using ElementName
:
<UserControl x:Name="fileBrowserControl">
...
<TextBlock Text="{Binding Label, ElementName=fileBrowserControl}"/>
<Button Grid.Column="1" Content=" ... " Click="BrowseButton_Click"/>
<TextBox Text="{Binding FilePath, ElementName=fileBrowserControl}"/>
...
</UserControl>
OR
If you want to keep DataContext for UserControl, you have to change code in Window to point to Window's DataContext explicitly like this:
<cust:FileBrowserTextBox Label="Input File"
FilePath="{Binding DataContext.InputFile,
RelativeSource={RelativeSource FindAncestor,
AncestorType=Window}}"/>
In place of RelativeSource, you can also use ElementName in binding by giving x:Name
to your window and bind using ElementName like explained in case of UserControl.
Upvotes: 3