Reputation: 25
I'm using a ListBox with a DataTemplate.
<ListBox Grid.Row="1" Grid.ColumnSpan="3" Grid.RowSpan="3" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
ItemsSource="{Binding Order.OrderLines}" >
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0">Qty</TextBlock>
<TextBox Text="{Binding LineQty, Mode=TwoWay}" Grid.Column="1" />
<TextBlock Grid.Row="1" Grid.Column="0">Weight</TextBlock>
<TextBox Text="{Binding Path=LineWeight}" Grid.Row="1" Grid.Column="1" />
<TextBlock Grid.Column="0" Grid.Row="2">Pallet Weights</TextBlock>
<TextBox Text="{Binding PalletWeights}" Grid.Row="2" Grid.Column="1" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The TextBox value is binding properly. The problem is I have a property on my ViewModel called "ViewMode" which I have the IsEnabled property of the TextBox bound to an App.xaml Style DataTrigger:
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding ViewMode}" Value="Add">
<Setter Property="BorderBrush" Value="White"></Setter>
<Setter Property="BorderThickness" Value="2,2,0,0"></Setter>
<Setter Property="BorderBrush" Value="Black"></Setter>
<Setter Property="BorderThickness" Value="0,0,2,2"></Setter>
<Setter Property="Background" Value="LightBlue"></Setter>
<Setter Property="Foreground" Value="Black"></Setter>
<Setter Property="FontWeight" Value="Bold"></Setter>
<Setter Property="IsEnabled" Value="true"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding ViewMode}" Value="Edit">
<Setter Property="BorderBrush" Value="White"></Setter>
<Setter Property="BorderThickness" Value="2,2,0,0"></Setter>
<Setter Property="BorderBrush" Value="Black"></Setter>
<Setter Property="BorderThickness" Value="0,0,2,2"></Setter>
<Setter Property="Background" Value="LightBlue"></Setter>
<Setter Property="Foreground" Value="Black"></Setter>
<Setter Property="FontWeight" Value="Bold"></Setter>
<Setter Property="IsEnabled" Value="true"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding ViewMode}" Value="View">
<Setter Property="IsEnabled" Value="false"></Setter>
<Setter Property="Foreground" Value="Gray"></Setter>
</DataTrigger>
</Style.Triggers>
<Setter Property="Margin" Value="2" />
</Style>
This works for all of my other TextBoxes. How do I get the IsEnabled property to work from within the DataTemplate? The DataContext of the ListBox is referencing the ViewModel property "Order", so I would think it should be able to see the ViewModel property "ViewMode".
Thanks, -Sid.
Upvotes: 0
Views: 3960
Reputation: 81253
You need to use RelativeSource
markup extension in your Binding in case you want to travel up to the Visual Tree like this -
<DataTrigger Binding="{Binding DataContext.ViewMode,
RelativeSource={RelativeSource FindAncestor,
AncestorType = UserControl}}"
Value="Add">
</DataTrigger>
By default DataContext
for your TextBox
will be an object of OrderLine
where is your property LineQty
resides. So, style is searching for ViewMode
property in an object of OrderLine
instead of your ViewModel so you need to use the RelativeSource to explicitly ask it to search it in DataContext
of your UserControl which is your ViewModel
.
Upvotes: 1
Reputation: 1290
Inside a DataTemplate, you don't get access to the properties from the ViewModel directly (you don't 'inherit' the DataContext). Assuming your ViewModel is the DataContext of the whole view, you can create a proxy:
class BindingProxy : Freezable
{
#region Freezable Members
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
#endregion
/// <summary>
/// Saves the DataContext from the whole view
/// </summary>
public object DataContext
{
get { return (object)GetValue(DataContextProperty); }
set { SetValue(DataContextProperty, value); }
}
public static readonly DependencyProperty DataContextProperty =
DependencyProperty.Register("DataContext", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
}
Then, in your XAML file, you need to reference the namespace where BindingProxy is located:
<UserControl xmlns:utilities="clr-namespace:MyApp.NamespaceWhereBindingProxyIsLocated"
...
Later, you create aan instance of BindingProxy for your view and link it with the view's DataContext (notice the DataContext={Binding} part):
<UserControl.Resources>
<utilities:BindingProxy x:Key="proxy" DataContext="{Binding}"/>
</UserControl.Resources>
Finally, you can use it as the DataContext for each TextBox:
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
..
<TextBox Text="{Binding Path={TemplateBinding DataContext.LineQty}, Mode=TwoWay}" DataContext="{Binding Source={StaticResource proxy}, Path=DataContext}"/>
..
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
Hope it helps
Upvotes: 1