davor
davor

Reputation: 969

Binding one control to another's DataContext

I bind my wpf window to app layer class (WindowVM.cs) using DataContext in Window.xaml.cs constructor (DataContext = WindowVM). But, one control (btnAdd) I want to bind to Window.xaml.cs property. So in Window.xaml.cs constructor I add this.btnAdd.DataContext. This is Window.xaml.cs constructor and property to which I want bind Button btnAdd:

    public Window()
    {
        InitializeComponent();
        DataContext = WindowVM;
        this.btnAdd.DataContext = this;
    }

    public RelayCommand Add
    {
        get
        {
            return _add == null ? _add= new RelayCommand(AddPP, CanAddPP) : _add;
        }
        set
        {
            OnPropertyChanged("Add");
        }
    }

Xaml looks like this (class PP is WindowVM property):

<TextBox Name="txtName" Text="{Binding PP.Name, ValidatesOnDataErrors=true, UpdateSourceTrigger=PropertyChanged}" />
<TextBox Name="txtSurname" Text="{Binding PP.Surname, ValidatesOnDataErrors=true, UpdateSourceTrigger=PropertyChanged}" />
<Button Command="{Binding Add}" Content="Add" ... />

And - everything works, but console output this:

BindingExpression path error: 'Add' property not found on 'object' ''WindowVM'...

In next calls there isn't any console output error for property Add.

Now I am a little bit confused because of this error. Is this error because of first DataContext (to WindowVM), because there isn't property Add, but with line this.btnAdd.DataContext property Add is found and it's the reason that it works?

Upvotes: 3

Views: 5052

Answers (3)

Rafael Costa
Rafael Costa

Reputation: 298

Simply set the DataContext of the Button in the XAML using a RelativeSource:

<Button Command="{Binding Add}" Content="Add" DataContext="{Binding Add, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" />

Upvotes: 2

user1656632
user1656632

Reputation:

I had this problem and I know this is an oldish post but I think this might help someone who stumbles on this in the future.

what I did was declare the viewmodels as resources

<Page.Resources>
    <local:LocationListViewModel x:Key="LocationList" />
    <local:LocationNewViewModel x:Key="NewLocation" />
    <code:BinaryImageConverter x:Key="imgConverter" />
</Page.Resources>

then which ever control I wanted to be associated with said viewmodel I added this to their datacontext

<TabItem x:Name="tabSettingsLocations" x:Uid="tabSettingsLocations" 
                 Header="Locations"
                 DataContext="{StaticResource ResourceKey=LocationList}">....

<TabItem x:Name="tbSettingsLocationsAdd" x:Uid="tbSettingsLocationsAdd" 
         Header="Add New"
         DataContext="{StaticResource ResourceKey=NewLocation}">....

<Image x:Name="imgSettingsLocationMapNew" x:Uid="imgSettingsLocationMapNew" 
         Source="{Binding Map, Converter={StaticResource imgConverter}, 
         Mode=TwoWay}"
         DataContext="{StaticResource ResourceKey=NewLocation}" />

So in my example above I have Listview bound to the list viewmodel and I create a new single location for my new entry. You will notice that by creating it as a resource I can bind the tabitem and the image (which is not a child of the tab item) to the new location viewmodel.

My command for the addnew location is in the new location viewmodel.

<TabItem x:Name="tbSettingsLocationsAdd" x:Uid="tbSettingsLocationsAdd" 
         Header="Add New"
         DataContext="{StaticResource ResourceKey=NewLocation}">....

    <Button x:Name="btnSettingsLocationSaveAdd" x:Uid="btnSettingsLocationSaveAdd" Content="Submit" Margin="0,80,10,0" 
        VerticalAlignment="Top" Style="{DynamicResource ButtonStyle}" HorizontalAlignment="Right" Width="75"
        Command="{Binding AddCommand}" />.....

Which is the child of the tabitem I bound to the new location viewmodel.

I hope that helps.

Upvotes: 1

NikoR
NikoR

Reputation: 558

When you set the DataContext-Property, your Window resets the Bindings of it's child controls. Even the Binding of your button. At this Point (before "button.DataContext = this" is evaluated) "Add" is searched in WindowVM. After this you set the Window class as buttons DC, and everything works fine.

To avoid the initial error, swap two lines from this

public Window()
{
    InitializeComponent();
    DataContext = WindowVM;
    this.btnAdd.DataContext = this;
}

to this

public Window()
{
    InitializeComponent();
    this.btnAdd.DataContext = this;
    DataContext = WindowVM;
}

Upvotes: 0

Related Questions