I am creating a WPF custom control, a Button with an Image and Text. I have added two dependency properties to the control, ImagePath and Text, and the control template (in Themes\Generic.xaml) is a simple stack panel that arranges the image and text horizontally.
The Text property works fine. But for some reason, the sample image in my test project doesn't appear when I use TemplateBinding to the ImagePath dependency property to get its path. I have tested the image by temporarily replacing the TemplateBinding in the custom control with a path to the image, in which case it appears.
I am hoping that someone with more experience in this area can take a look and tell me why the control isn't working as expected. Thanks for your help.
My VS 2008 solution contains one project, CustomControlDemo. The project contains a custom control, TaskButton.cs, and a main window, Window1.xaml, that I use to test the control. My test image, calendar.png, is located in a Resources folder at the root level of the project, and Generic.xaml is located in a Themes folder, also at the root level of the project.
Here is the code for my custom control (from TaskButton.cs):
using System.Windows;
using System.Windows.Controls;
namespace CustomControlDemo
{
public class TaskButton : RadioButton
{
#region Fields
// Dependency property backing variables
public static readonly DependencyProperty ImagePathProperty;
public static readonly DependencyProperty TextProperty;
#endregion
#region Constructors
/// <summary>
/// Default constructor.
/// </summary>
static TaskButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(TaskButton), new FrameworkPropertyMetadata(typeof(TaskButton)));
// Initialize ImagePath dependency properties
ImagePathProperty = DependencyProperty.Register("ImagePath", typeof(string), typeof(TaskButton), new UIPropertyMetadata(null));
TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(TaskButton), new UIPropertyMetadata(null));
}
#endregion
#region Dependency Property Wrappers
/// <summary>
/// The ImagePath dependency property.
/// </summary>
public string ImagePath
{
get { return (string)GetValue(ImagePathProperty); }
set { SetValue(ImagePathProperty, value); }
}
/// <summary>
/// The Text dependency property.
/// </summary>
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
#endregion
}
}
And here is the control template (from Generic.xaml):
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CustomControlDemo">
<Style TargetType="{x:Type local:TaskButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:TaskButton}">
<StackPanel Height="Auto" Orientation="Horizontal">
<Image Source="{TemplateBinding ImagePath}" Width="24" Height="24" Stretch="Fill"/>
<TextBlock Text="{TemplateBinding Text}" HorizontalAlignment="Left" Foreground="{DynamicResource TaskButtonTextBrush}" FontWeight="Bold" Margin="5,0,0,0" VerticalAlignment="Center" FontSize="12" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
And finally, here is the Window1 markup that I am using to test the control:
<Window x:Class="CustomControlDemo.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:customControl="clr-namespace:CustomControlDemo"
Title="Window1" Height="300" Width="300">
<Grid>
<customControl:TaskButton ImagePath="Resources\calendar.png" Text="Calendar" />
</Grid>
</Window>
Any ideas why the image path isn't working? Thanks again.
I am going to leave cwap's answer as the accepted answer, because it is technically correct. However, it turns out that there is an easier way to solve this problem.
TemplateBindings aren't first-class Binding objects. They are designed to be lightweight, so they are one-way, and they lack some features of other Binding objects. Most notably, they don't support known type converters associated with a target. See MacDonald, Pro WPF in C# 2008, p. 872. That's why cwap responds correctly that I would probably need to create a type converter and reference it specifically in the control template for my custom button.
But I don't have to use a TemplateBinding to bind the control template to the ImagePath property of my custom control. I can use a plain old Binding object. Here is the revised markup for my custom control's template:
<!-- Task Button Default Control Template-->
<Style TargetType="{x:Type local:TaskButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:TaskButton}">
<StackPanel Height="Auto" Orientation="Horizontal">
<Image Source="{Binding Path=ImagePath, RelativeSource={RelativeSource TemplatedParent}}" Width="24" Height="24" Stretch="Fill" Margin="10,0,0,0" />
<TextBlock Text="{TemplateBinding Text}" HorizontalAlignment="Left" Foreground="{TemplateBinding Foreground}" FontWeight="Bold" Margin="5,0,10,0" VerticalAlignment="Center" FontSize="12" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
If you look at the ImageControl in the template, you can see the change. Note the RelativeSource property in the same object. Setting this property to ={RelativeSource TemplatedParent} is what lets me enter a relative path in my Window1 instance of the TaskButton and have it resolved correctly in the custom control.
So my recommendation for others researching this thread would be to skip the value converter and simply switch from TemplateBinding to Binding for the Image property.
Thanks also to Marco Zhou, who provided this answer to a similar question in the MSDN WPF forum.
Image doesn't take a string as a source :) You can see this in intellisense. You need to bind on an ImageSource (Or use an IValueConverter to convert the string to an ImageSource)
See this question for some tips on how to do this conversion.
Actually neither of these answers are correct.
{TemplateBinding ImagePath} is nothing more than a shortcut for {Binding Path=ImagePath, RelativeSource={RelativeSource TemplatedParent}} and as such is almost completely identical.
Also if you provide a string for ImagePath it will correctly resolve to an ImageSource although you take a hit in application performance. The real issue has to do with relative and absolute image path on the supplied ImagePath="Resources\calendar.png" in the xaml for the test. This clues the compiler to think that the supplied path is absolute because of the use of \ instead of / in defining the path.
The reason that the long form of the binding works and the shortcut doesn't is that it provides clues to the compiler that the source of the image supplied (Resources\calendar.png) is a relative path not an absolute path, therefore the image is found and the binding works. If you debug the binding you will see that the shortcut tries resolve the supplied string into an image source but can not find the file "Resources\calendar.png" If you provide a full URI to the image i.e "C:\...\Resources\calendar.png" or the corresponding blend notation of "/application;component/Resources/calendar.png" then the image will be found and the binding resolved.
This point becomes really important when you are trying to reference images from an external source instead of those compiled as resources into the final compilation.
simple way(tested)
1-make your valueConverter like this
public class objectToImageSourceConverter:IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string packUri =value.ToString();
ImageSource Source = new ImageSourceConverter().ConvertFromString(packUri) as ImageSource;
return Source;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
2-bind your Image Source to parent's string properety (i used "tag" property)
like this xaml:
<Image HorizontalAlignment="Right" Height="Auto" Margin="0,11.75,5.5,10.75" VerticalAlignment="Stretch" Width="40.997" Source="{Binding Path=Tag, RelativeSource={RelativeSource TemplatedParent}}"/>
Related
I have a resource that need to be a different color depending on where it is used, so I use this attached property:
public static class AssetProperties
{
public static Brush GetFillBrush(DependencyObject obj)
{
return (Brush)obj.GetValue(FillBrushProperty);
}
public static void SetFillBrush(DependencyObject obj, Brush value)
{
obj.SetValue(FillBrushProperty, value);
}
public static readonly DependencyProperty FillBrushProperty =
DependencyProperty.RegisterAttached("FillBrush",
typeof(Brush),
typeof(AssetProperties),
new FrameworkPropertyMetadata(new BrushConverter().ConvertFrom("#FFE41300"), FrameworkPropertyMetadataOptions.Inherits));
}
We define the symbol and use it something like this in a window or user control (this is of course a lot simplified, the resource is for example defined in a separate file) :
<Grid>
<Grid.Resources>
<ResourceDictionary>
<Rectangle x:Key="SomeColorfulSymbol" x:Shared="False" Width="10" Height="10"
Fill="{Binding (main:AssetProperties.FillBrush), RelativeSource={RelativeSource Self}}" />
</ResourceDictionary>
</Grid.Resources>
<ContentControl Content="{StaticResource SomeColorfulSymbol}" main:AssetProperties.FillBrush="Blue"/>
</Grid>
This works as intended, a nice blue rectangle appears. Without setting the attached property, the rectangle is the default red of the FillBrush attached property.
The problem is when we try to use the symbol inside a custom user control defined like this:
OuterControl.xaml:
<UserControl x:Class="AttachedPropertyResourceTest.OuterControl"
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="300" d:DesignWidth="300">
<Grid>
<StackPanel>
<TextBlock Text="Some title"/>
<ContentControl Content="{Binding InnerContent, RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}}"/>
</StackPanel>
</Grid>
</UserControl>
OuterControl.xaml.cs:
[ContentProperty("InnerContent")]
public partial class OuterControl
{
public FrameworkElement InnerContent
{
get { return (FrameworkElement)GetValue(InnerContentProperty); }
set { SetValue(InnerContentProperty, value); }
}
public static readonly DependencyProperty InnerContentProperty =
DependencyProperty.Register("InnerContent", typeof(FrameworkElement), typeof(OuterControl), new FrameworkPropertyMetadata(null));
public OuterControl()
{
InitializeComponent();
}
}
Now if I wrap the ContentControl in the above snippet like this instead:
<main:OuterControl>
<ContentControl Content="{StaticResource SomeColorfulSymbol}"/>
</main:OuterControl>
it looks good in the VS designer, a title plus a rectangle that is the default red of FillBrush. In runtime however we only get the title. The rectangle gets no color (UnsetValue) and we get this binding error:
System.Windows.Data Error: 40 : BindingExpression path error:
'(main:AssetProperties.FillBrush)' property not found on 'object'
''Rectangle' (Name='')'.
BindingExpression:Path=(main:AssetProperties.FillBrush);
DataItem='Rectangle' (Name=''); target element is 'Rectangle'
(Name=''); target property is 'Fill' (type 'Brush')
If I add an invisible instance of the symbol before the wrapped one, it works again, i.e., a red rectangle appears:
<ContentControl Content="{StaticResource SomeColorfulSymbol}" Visibility="Collapsed"/>
<main:OuterControl>
<ContentControl Content="{StaticResource SomeColorfulSymbol}"/>
</main:OuterControl>
One problem is that the attached property is not registered, when I put a breakpoint on the RegisterAttached method it is not called without the extra invisible ContentControl. This is however only a part of the problem, for example forcing the registration like this does not work:
<StackPanel>
<TextBlock Text="I'm red!" Background="{Binding (main:AssetProperties.FillBrush), RelativeSource={RelativeSource Self}}"/>
<main:OuterControl>
<ContentControl Content="{StaticResource SomeColorfulSymbol}"/>
</main:OuterControl>
</StackPanel>
The text "I'm red" is actually red and the attached property is registered, but we get the exact same binding error.
I also tried without the ContentProperty["InnerContent"], setting the InnerContent attribute explicitly in xaml, with the same result.
Could someone shed some light on this?
Maybe using a control template instead of OuterControl wouldn't have this problem (?), but there is a lot of behavior associated with OuterControl and I would prefer this approach.
To prevent the following issue, try specifying the path property explicitly like:{Binding Path=(main:....}
<Rectangle x:Key="SomeColorfulSymbol" x:Shared="False" Width="10" Height="10" Fill="{Binding Path=(main:AssetProperties.FillBrush), RelativeSource={RelativeSource Self}}" />
I want to offer a context menu with an item that has a color swatch in the space where "icons" are normally placed for such menu items, i.e. the space corresponding to MenuItem.Icon.
But the color swatch is dynamic--a Brush property on the UserControl that (in this crafted example) changes to a random color in response to the ContextMenuOpening event--and my attempt at binding to it is failing.
When run, the menu item has no content in the Icon space, and Visual Studio's output contains an error that doesn't seem like it ought to be happening.
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.ContextMenu', AncestorLevel='1''. BindingExpression:Path=PlacementTarget.RandomBrush; DataItem=null; target element is 'Rectangle' (Name=''); target property is 'Fill' (type 'Brush')
Here's the XAML for the control:
<UserControl x:Class="ContextMenuItemIconTest.UserControl1"
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="300" d:DesignWidth="300">
<UserControl.ContextMenu>
<ContextMenu>
<MenuItem Header="Do something">
<MenuItem.Icon>
<Rectangle Width="16" Height="16" Fill="{Binding PlacementTarget.RandomBrush, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}" />
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</UserControl.ContextMenu>
<Grid>
</Grid>
And the code behind:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace ContextMenuItemIconTest
{
/// <summary>
/// Interaction logic for UserControl1.xaml
/// </summary>
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
ContextMenuOpening += UserControl1_ContextMenuOpening;
}
void UserControl1_ContextMenuOpening(object sender, ContextMenuEventArgs e)
{
Random r = new Random();
RandomBrush = new SolidColorBrush(Color.FromRgb((byte)r.Next(256), (byte)r.Next(256), (byte)r.Next(256)));
}
#region RandomBrush (Dependency Property)
public Brush RandomBrush
{
get { return (Brush)GetValue(RandomBrushProperty); }
set { SetValue(RandomBrushProperty, value); }
}
public static readonly DependencyProperty RandomBrushProperty =
DependencyProperty.Register(
"RandomBrush",
typeof(Brush),
typeof(UserControl1),
new PropertyMetadata(new SolidColorBrush(Colors.Blue)));
#endregion
}
}
Not sure if there is a better solution but I think the scenario here is very tricky. The Icon content seems to be detached completely from the visual tree. So you cannot use Binding with RelativeSource or ElementName and strangely even setting the Source to some {x:Reference} causes some cyclic reference error.
I just could think of this work-around, a little hacky but it's acceptable. There is an interesting knowledge about the Freezable object. Binding inside it (set for some property) can use RelativeSource as well as ElementName even when it's just declared as a resource (added to Resources). So in this case we can try using some object deriving from Freezable to act as the proxy. Because this proxy is declared as a resource, we can set the Binding inside Icon with Source being set to some StaticResource referencing the proxy. Then it would work. There are many objects deriving from Freezable for your choice, you can even create your own class. But I would like to use something existing here. The DiscreteObjectKeyFrame is the most suitable object to use. Technically its Value property can hold any kind of object. Now's the working code:
<ContextMenu>
<ContextMenu.Resources>
<!-- the proxy here -->
<DiscreteObjectKeyFrame x:Key="o" KeyTime="0"
Value="{Binding PlacementTarget.RandomBrush,
RelativeSource={RelativeSource AncestorType=ContextMenu}}"/>
</ContextMenu.Resources>
<MenuItem Header="Do something">
<MenuItem.Icon>
<Rectangle Width="16" Height="16"
Fill="{Binding Value, Source={StaticResource o}}" />
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
I am not sure if my question header represent exactly my problem, I will do the best to explain:
I have a grid cell DataTemplate: (the grid belong to third party company but it`s not important for my question)
<DataTemplate>
<TextBlock>
<Hyperlink Command="{Binding OpenLinkCommand}">
<Hyperlink.ToolTip>
<TextBlock Text="{Binding Data.MapLink}"/>
</Hyperlink.ToolTip>
<TextBlock Text="{Binding Data.MapLink}" TextDecorations="underline">
</Hyperlink>
</TextBlock>
</DataTemplate>
I want make this DataTemplate to show some hyperlink ("Data.MapLink" is the object which contain the link value) and each click on this link will fire the command "OpenLinkCommand".
The problem is that "Data.MapLink" and "OpenLinkCommand" are located in different dataContext and then I have to choose one of the next choices:
leave hyperlink dataContext as it - the command won`t work and the hyperlink will get the Data.MapLink value.
change hyperlink dataContext to the command datacontext - The command will work but the hyperlink name will be empty.
Regretfully I don`t have option put those items in same dataContext so I must find a way how to tell the command that it dataContext is "X" and tell the hyperLink that it dataContext is "Y".
I am hoping that my question is clear
How can I solve this problem?
There are some binding properties you can use to specify a different Source for your binding than the default DataContext
The most common ones are ElementName or RelativeSource, which will find another UI element in the VisualTree so you can bind to it's properties.
For example, the following uses ElementName to tell the binding that it should use MyGridView as the binding source, and to bind to MyGridView.DataContext.OpenLinkCommand
<Hyperlink Command="{Binding ElementName=MyGridView,
Path=DataContext.OpenLinkCommand}">
You can also use RelativeSource in a binding to find an object further up the VisualTree of the specified object type, and use it as the binding source. This example does the same thing as the above example, except it uses RelativeSource instead of ElementName, so your GridView doesn't need to have a Name specified.
<Hyperlink Command="{Binding
RelativeSource={RelativeSource AncestorType={x:Type GridView}},
Path=DataContext.OpenLinkCommand}">
A third option is to set the binding's Source property to a static object, like this:
<Hyperlink Command="{Binding
Source={x:Static local:MyStaticClass.OpenLinkCommand}}">
Based on your comment here about binding to a singleton, this would probably be the best option for you.
You will have to have an instance of the desired data context (usually in the resources of a control or window). Once you have that, you should be able to explicitly set the data context of the textblock instead of inheriting the parent data context automatically.
For example:
<TextBlock DataContext="{StaticResource MyDataMapLinkDataContext}" Text="{Binding Data.MapLink}" TextDecorations="underline"/>
If you really do need to use another property for an extra data context then you can just use an attached property.
XAML
<Window.Resources>
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<ContentPresenter Content="{Binding (local:ExtraDataContextProvider.ExtraDataContext), RelativeSource={RelativeSource TemplatedParent}}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<Button Margin="172,122,131,79" Foreground="Green" local:ExtraDataContextProvider.ExtraDataContext="A test">
test
</Button>
</Grid>
</Window>
Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApplication1
{
public class ExtraDataContextProvider : DependencyObject
{
public static object GetExtraDataContext(DependencyObject obj)
{
return (object)obj.GetValue(ExtraDataContextProperty);
}
public static void SetExtraDataContext(DependencyObject obj, object value)
{
obj.SetValue(ExtraDataContextProperty, value);
}
public static readonly DependencyProperty ExtraDataContextProperty = DependencyProperty.RegisterAttached("ExtraDataContext", typeof(object), typeof(ExtraDataContextProvider), new PropertyMetadata(null));
}
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}
I'm sure this is a real beginner question; I'm just having trouble figuring out how to search for it.
I have a simple UserControl (MyNewControl) that only has three controls, one of which is the following label:
<sdk:Label x:Name="Title" />
In another control, then, I want to use MyNewControl, like this:
<local:MyNewControl Grid.Column="1" x:Name="MyNewGuy" />
What do I need to do so that this second control can, for example, set a gradient background for my Title label?
First you define the desired dependency property in your UserControl:
public partial class MyUserControl : UserControl
{
public Brush LabelBackground
{
get { return (Brush)GetValue(LabelBackgroundProperty); }
set { SetValue(LabelBackgroundProperty, value); }
}
public static readonly DependencyProperty LabelBackgroundProperty =
DependencyProperty.Register("LabelBackground", typeof(Brush), typeof(MyUserControl), new PropertyMetadata(null));
public MyUserControl()
{
InitializeComponent();
}
}
To assign the value of your property to the child label, you can bind using the ElementName property of the binding:
<UserControl x:Class="SilverlightApplication1.MyUserControl"
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:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
d:DesignHeight="300"
d:DesignWidth="400"
mc:Ignorable="d"
x:Name="UserControl"
>
<Grid x:Name="LayoutRoot">
<sdk:Label x:Name="Title"
HorizontalAlignment="Center"
VerticalAlignment="Center" Content="Title" Background="{Binding LabelBackground, ElementName=UserControl}" />
</Grid>
</UserControl>
As you are using Silverlight 5, you can also set a RelativeSource to your binding, instead of internally naming your UserControl:
<sdk:Label Background="{Binding LabelBackground, RelativeSource={RelativeSource AncestorType=UserControl}}" />
Then, when using your UserControl, you just set (or bind) the LabelBackground to the desired value:
<local:MyUserControl LabelBackground="Red"/>
Just to note, you can also create a CustomControl instead of a UserControl, add the dependency property to it the same way and use a TemplateBinding when defining its template.
You can do that using dependency property in your custom control . Say you defined LableBG as an dependency property in your custom control and do the binding with an Background of your defined Label control in xaml . And when you use your custom control in another control you can set the LableBG of it from xaml or else from code behind.
Note : the type of your defined dependency property should be of Brush
For eg :
Defining Dependency Property in cs file of your custom control :
/1. Declare the dependency property as static, readonly field in your class.
public static readonly DependencyProperty LableBGProperty = DependencyProperty.Register(
"LableBG", //Property name
typeof(Brush), //Property type
typeof(MySilverlightControl), //Type of the dependency property provider
null );//Callback invoked on property value has changes
<sdk:Label x:Name="Title" Background="{Binding LableBG }" /> (Custom Control)
<local:MyNewControl Grid.Column="1" x:Name="MyNewGuy" LableBG="Red" /> (Another control)
I need to access the container's DataContext from a UserControl (a grid containing textboxes and a listbox: I need to insert items in this list box) that I created in WPF: which is the best way to do it?
I was thinking to pass the DataContext as parameter to user control but think there is a cleaner way to do it.
Normally the DataContext will be inherited, just do not explicitly set it on the UserControl and it will get it from its parent. If you have to set it you could still use the Parent property to get the parent, which you then can safe-cast to a FrameworkElement and if it is not null you can grab its DataContext.
I sometimes have nested User controls and a grandchild usercontrol sometimes needs the grandparent's view's data context. The easiest way I have found so far (and I'm somewhat of a newbie) is to use the following:
<Shared:GranchildControl DataContext="{Binding RelativeSource={RelativeSource
FindAncestor, AncestorType={x:Type GrandparentView}},
Path=DataContext.GrandparentViewModel}" />
I wrote up a more detailed example on my blog if you want more specifics.
Add this BindingProxy class to your project:
using System.Windows;
namespace YourNameSpace
{
/// <summary>
/// Add Proxy <ut:BindingProxy x:Key="Proxy" Data="{Binding}" /> to Resources
/// Bind like <Element Property="{Binding Data.MyValue, Source={StaticResource Proxy}}" />
/// </summary>
public class BindingProxy : Freezable
{
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
public object Data
{
get { return (object)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
public static readonly DependencyProperty DataProperty = DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy));
}
}
Add the BindingProxy to your UserControl's resources.
Set the 'Data' property of the BindingProxy to whatever you need, e.g. search for a parent Window. Data="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}},Path=DataContext}" If you needed something more complex you could use a custom converter.
Now you have access to that parent's DataContext: {Binding Data.MyCommand, Source={StaticResource BindingProxy}}
<UserControl
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"
xmlns:common="clr-namespace:YourNameSpace;assembly=YourAssembly"
mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<common:BindingProxy x:Key="BindingProxy" Data="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}},Path=DataContext}" />
</UserControl.Resources>
<Border>
<Button Command="{Binding Data.MyCommand, Source={StaticResource BindingProxy}}">Execute My Command</Button>
<!-- some visual stuff -->
</Border>
</UserControl>
H.B. answers the question in your title.
However the text poses a different design question. I'd ask you to reconsider your design.
A control inherits the DataContext property of its ancestor as long as no one in between explicitly overrides.
If the user control needs data, it should get it from its data source (a viewmodel for the user control). So in this case, the user control can obtain the data it needs from the ListItemsForDisplay property exposed on the SomeViewModel instance. No need to get parent and cast.. much cleaner.
<ContainerType DataSource={Binding SomeViewModel}>
<YourUserControl>
<ListBox ItemsSource={Binding ListItemsForDisplay}"/>
...
In this case, UserControl will get DataContext windows
<Window>
<local:MyUserControl DataContext="{Binding}"/>
</Window>