|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
Note: This is an unedited contribution. If this article is inappropriate,
needs attention or copies someone else's work without reference then please
Report This Article
IntroductionThis article discusses and demonstrates three ways to simulate having an inheritance context for BackgroundYou can register a dependency property with the WPF property system so that its value inherits down the element tree, technically the logical tree. The canonical example is the Another inheritable dependency property on both the This system breaks down when you try to bind a property on an object that is not in the element tree. One example of this is if you try to bind a property on the object referenced by an element’s This article shows three ways to work around the problem of not having an inheritance context when data binding. Each technique manages to “export” an element tree’s The DemoAt the top of this article, you can download the demo project that accompanies this article. The app shows how to do the same task three ways. The task itself is rather trivial, and you could definitely implement it without the need for artificial inheritance contexts. However, as with many of my articles, I struggled to come up with a programming task that is simple enough to not “get in the way” yet complicated enough to allow me to demonstrate the technique under review. The demo app allows the user to choose a historic document, such as the US Constitution, and view its name displayed in large text. The document name paints with a brush that displays a photograph of the document. The UI also contains a As seen in the screenshot below, when viewing the US Constitution, the text paints with a photo of the US Constitution at full opacity:
Resource InjectionThe quickest and least complicated way to gain access to a The other trick to know is that the resource must be added to Here is the code-behind for the public partial class ResourceInjectionDemo : UserControl
{
public ResourceInjectionDemo()
{
// After setting our DataContext, inject it into the App's
// Resources so that it is visible to all DynamicResource references.
// NOTE: This must be done *before* the call to InitializeComponent
// since DynamicResource references for objects not in the logical tree
// only check the App's Resources once, upon creation.
// This only works once. After the call to InitializeComponent, updating
// the resource value to a new datacontext object will have no effect.
base.DataContext = HistoricDocument.GetDocuments();
App.Current.Resources["DATA_HistoricDocuments"] = base.DataContext;
this.InitializeComponent();
}
} The resource key <UserControl
x:Class="ArtificialInheritanceContextDemo.ResourceInjectionDemo"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ComboBox
Grid.Row="0"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding}"
Margin="4"
/>
<ListBox x:Name="_listBox" Grid.Row="1" Margin="4">
<ListBoxItem Content="Fully Opaque" IsSelected="True">
<ListBoxItem.Tag>
<!--
Get a reference to the DataContext by grabbing it from
the Application's Resources via a DynamicResource reference.
-->
<Image
DataContext="{DynamicResource DATA_HistoricDocuments}"
Opacity="1"
Source="{Binding PhotoUri}"
Width="300" Height="350"
/>
</ListBoxItem.Tag>
</ListBoxItem>
<ListBoxItem Content="Semi-Transparent">
<ListBoxItem.Tag>
<Image
DataContext="{DynamicResource DATA_HistoricDocuments}"
Opacity="0.5"
Source="{Binding PhotoUri}"
Width="300" Height="350"
/>
</ListBoxItem.Tag>
</ListBoxItem>
</ListBox>
<Viewbox Grid.Row="2" Stretch="Fill">
<TextBlock
FontWeight="Bold"
HorizontalAlignment="Center" VerticalAlignment="Center"
Text="{Binding Path=Name}"
>
<TextBlock.Foreground>
<VisualBrush
Visual="{Binding ElementName=_listBox, Path=SelectedItem.Tag}"
/>
</TextBlock.Foreground>
</TextBlock>
</Viewbox>
</Grid>
</UserControl>
DataContextSpyA spy is a person who secretly examines the actions and information of other individuals or organizations and reports it to an external party. My Here is the public class DataContextSpy
: Freezable // Enable ElementName and DataContext bindings
{
public DataContextSpy()
{
// This binding allows the spy to inherit a DataContext.
BindingOperations.SetBinding(this, DataContextProperty, new Binding());
this.IsSynchronizedWithCurrentItem = true;
}
/// <summary>
/// Gets/sets whether the spy will return the CurrentItem of the
/// ICollectionView that wraps the data context, assuming it is
/// a collection of some sort. If the data context is not a
/// collection, this property has no effect.
/// The default value is true.
/// </summary>
public bool IsSynchronizedWithCurrentItem { get; set; }
public object DataContext
{
get { return (object)GetValue(DataContextProperty); }
set { SetValue(DataContextProperty, value); }
}
// Borrow the DataContext dependency property from FrameworkElement.
public static readonly DependencyProperty DataContextProperty =
FrameworkElement.DataContextProperty.AddOwner(
typeof(DataContextSpy),
new PropertyMetadata(null, null, OnCoerceDataContext));
static object OnCoerceDataContext(DependencyObject depObj, object value)
{
DataContextSpy spy = depObj as DataContextSpy;
if (spy == null)
return value;
if (spy.IsSynchronizedWithCurrentItem)
{
ICollectionView view = CollectionViewSource.GetDefaultView(value);
if (view != null)
return view.CurrentItem;
}
return value;
}
protected override Freezable CreateInstanceCore()
{
// We are required to override this abstract method.
throw new NotImplementedException();
}
}
This class uses the Hillberg Freezable Trick to gain access to the The <UserControl
x:Class="ArtificialInheritanceContextDemo.DataContextSpyDemo"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ArtificialInheritanceContextDemo"
>
<UserControl.DataContext>
<ObjectDataProvider
MethodName="GetDocuments"
ObjectType="{x:Type local:HistoricDocument}"
/>
</UserControl.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ComboBox
Grid.Row="0"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding}"
Margin="4"
/>
<ListBox x:Name="_listBox" Grid.Row="1" Margin="4">
<ListBox.Resources>
<!--
This object 'spies' on the DataContext for other elements.
-->
<local:DataContextSpy x:Key="spy" />
</ListBox.Resources>
<ListBoxItem Content="Fully Opaque" IsSelected="True">
<ListBoxItem.Tag>
<Image
DataContext="{Binding Source={StaticResource spy}, Path=DataContext}"
Opacity="1"
Source="{Binding PhotoUri}"
Width="300" Height="350"
/>
</ListBoxItem.Tag>
</ListBoxItem>
<ListBoxItem Content="Semi-Transparent">
<ListBoxItem.Tag>
<Image
DataContext="{Binding Source={StaticResource spy}, Path=DataContext}"
Opacity="0.5"
Source="{Binding PhotoUri}"
Width="300" Height="350"
/>
</ListBoxItem.Tag>
</ListBoxItem>
</ListBox>
<Viewbox Grid.Row="2" Stretch="Fill">
<TextBlock
FontWeight="Bold"
HorizontalAlignment="Center" VerticalAlignment="Center"
Text="{Binding Path=Name}"
>
<TextBlock.Foreground>
<VisualBrush
Visual="{Binding ElementName=_listBox, Path=SelectedItem.Tag}"
/>
</TextBlock.Foreground>
</TextBlock>
</Viewbox>
</Grid>
</UserControl> Virtual Branch of Logical TreeThe last technique we cover is something that I wrote an article about back in May 2007. I included a demonstration that uses a virtual branch in this article just for the sake of completeness. Creating a virtual branch of the logical tree is similar to using a Virtual branches are very flexible and you can easily use them to export more than just the Here is the code-behind for the public partial class VirtualBranchDemo : UserControl
{
public VirtualBranchDemo()
{
this.InitializeComponent();
base.DataContext = HistoricDocument.GetDocuments();
}
} The <UserControl
x:Class="ArtificialInheritanceContextDemo.VirtualBranchDemo"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ArtificialInheritanceContextDemo"
>
<UserControl.Resources>
<FrameworkElement x:Key="bridge" />
</UserControl.Resources>
<UserControl.DataContext>
<Binding
Mode="OneWayToSource"
Path="DataContext"
Source="{StaticResource bridge}"
/>
</UserControl.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ComboBox
Grid.Row="0"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding}"
Margin="4"
/>
<ListBox x:Name="_listBox" Grid.Row="1" Margin="4">
<ListBoxItem Content="Fully Opaque" IsSelected="True">
<ListBoxItem.Tag>
<Image
DataContext="{Binding Source={StaticResource bridge}, Path=DataContext}"
Opacity="1"
Source="{Binding PhotoUri}"
Width="300" Height="350"
/>
</ListBoxItem.Tag>
</ListBoxItem>
<ListBoxItem Content="Semi-Transparent">
<ListBoxItem.Tag>
<Image
DataContext="{Binding Source={StaticResource bridge}, Path=DataContext}"
Opacity="0.5"
Source="{Binding PhotoUri}"
Width="300" Height="350"
/>
</ListBoxItem.Tag>
</ListBoxItem>
</ListBox>
<Viewbox Grid.Row="2" Stretch="Fill">
<TextBlock
FontWeight="Bold"
HorizontalAlignment="Center" VerticalAlignment="Center"
Text="{Binding Path=Name}"
>
<TextBlock.Foreground>
<VisualBrush
Visual="{Binding ElementName=_listBox, Path=SelectedItem.Tag}"
/>
</TextBlock.Foreground>
</TextBlock>
</Viewbox>
</Grid>
</UserControl> Pros and Cons of Each TechniqueEach of these techniques has its own relative merits. I have listed all of the pros and cons that I could think of here to help make it easier to decide which approach to use. I am sure there must be other considerations that I have not listed, so please drop a comment on this article if you discover some. Resource InjectionPROS
CONS
DataContextSpyPROS
CONS
Virtual BranchPROS
CONS
Revision History
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||