ElementName not working when assigning DataTemplate from code behind? - silverlight

I am trying to access Control using ElementName from DataTemplate that is used in different UserControl (Resources) than defined (in xaml).
Imagine this situation:
MyUserControl.xaml with following DataTemplate in resources:
<UserControl.Resources>
<DataTemplate x:Key="SomeTemplate">
<TextBlock Text="{Binding Text, ElementName=TextElement}"/>
</DataTemplate>
</UserControl.Resources>
<Grid x:Name="LayoutRoot">
<TextBlock x:Name="TextElement" Text="IT WORKS! (not...)"/>
</Grid>
</UserControl>
MyUserControlWrapper.xaml
<ContentPresenter x:Name="ContentPresenter" Content="{Binding SomeContent}"/>
and in code behind of MyUserControlWrapper.xaml i set ContentTemplate of ContentPresenter from MyUserControl.xaml:
something like:
ContentPresenter.ContentTemplate = (DataTemplate)childView.Resources["SomeTemplate"];
Is it possible to use ElementName from resources that are defined outside UserControl?
How DataTemplate searches for ElementName in same UserControl then? Maybe its possible to set something like DataContext for DataTemplate itself for ElementName to work, without messing with DataContext that is sent to controls used inside Template?

You need to review the concepts related to Namescopes.
Briefly names are scoped at the point where a Xaml resources are loaded. For example each UserControl will each load their own Xaml and therefore have their own namescope. In your case you asking MyUserControlWrapper to find a name that its LoadComponent has not seen.

Maybe you can just walk up the VisualTree using RelativeSource and FindAncestor?
There is a nice presentation of different binding variants here:
http://www.wpfwiki.com/Default.aspx?Page=WPF%20Q5.3&AspxAutoDetectCookieSupport=1

Related

Josh Smith's legendary article: I need a bit more on the DataBinding that takes place

It’s about the MSDN article of Josh Smith on MVVM and the sample application. I know there are lots of question on SO about this topic, and I’ve explored them all. Most of them are focused on MVVM, but my issue is, I think, more XAML related than MVVM.
The sample application contains the following XAML for the Workspaces area -
<Border Grid.Column="2" Style="{StaticResource MainBorderStyle}">
<ContentControl Content="{Binding Path=Workspaces}" ContentTemplate="{StaticResource ResourceKey=WorkspacesTemplate}"/>
</Border>
and the related resources are -
//Explains how to render the 'Workspace' content area in the main window
<DataTemplate x:Key="WorkspacesTemplate">
<TabControl Margin="4" ItemsSource="{Binding}"
IsSynchronizedWithCurrentItem="True"
ItemTemplate="{StaticResource ResourceKey=ClosableTabItemTemplate}"/>
</DataTemplate>
//Explains how to render a tab item with a close button
<DataTemplate x:Key="ClosableTabItemTemplate">
<DockPanel Width="120">
<Button Command="{Binding Path=CloseCommand}" Content="X" Cursor="Hand"
DockPanel.Dock="Right" Focusable="False" FontFamily="Courier"
FontSize="9" FontWeight="Bold" Margin="0,1,0,0" Padding="0"
VerticalContentAlignment="Bottom" Width="16" Height="16"/>
<ContentPresenter Content="{Binding Path=DisplayName}" VerticalAlignment="Center"/>
</DockPanel>
</DataTemplate>
What I still don't understand -
The syntax ItemsSource="{Binding}" should bind the ItemsSource directly to the TabControl’s DataContext, rather than to any of the DataContext’s properties. But where exactly is the TabControl’s DataContext being set?
How exactly is Content="{Binding Path=Workspaces}" creating a binding between ItemsSource of the TabControl and the Workspaces (the ObservableCollection of WorkspaceViewModel)?
The article says By relying on data binding, the Content property of a TabItem receives a ViewModelBase-derived object to display. How ?!? Ok, through data binding. But that is just too much to abstract away, for me.
In general I’m missing the way binding is flowing/working through these two resources behind the scene to load the Views in the TabItems. To me it's like, what is causing what to be bound to what.
This legendary article and the sample application is something extremely useful for WPF/MVVM beginners. But it’s not much elaborative. I myself have learned using MVVM with this one. I think there is and will be some others like me. So, can anyone explain the binding sequences a little bit more elaborately please?
Relevant Note :
May be it'll give you a hint of what I already know in this context, and help you in answering. I'm a beginner level WPF application developer. With my not so good knowledge on XAML -
I’m aware of the magic through typed-DataTemplate of displaying the View when the ViewModel type occurs, and then setting the ViewModel as the DataContext of that View
My understanding is, Content tells what to display on a ContentControl and ContentTemplate tells how to display that Content.
I have a little more than basic data binding concept, and I've worked in some WPF/MVVM projects.
The syntax ItemsSource="{Binding}" should bind the ItemsSource
directly to the TabControl’s DataContext, rather than to any of the
DataContext’s properties. But where exactly is the TabControl’s
DataContext being set?
By virtue of being a data template, the TabControl will have its DataContext set (by WPF) to the data item it is templating. In fact, the root-level item in the data template will have its DataContext set by WPF. In this case, it's a TabControl.
Since the data template is assigned to the ContentControl's ContentTemplate property, it will automatically receive the ContentControl's Content as its DataContext.
How exactly is Content="{Binding Path=Workspaces}" creating a binding
between ItemsSource of the TabControl and the Workspaces (the
ObservableCollection of WorkspaceViewModel)?
I think my previous answer addresses this, but let me re-state it in a form that directly answers this question. The binding on the ContentControl ensures that the DataContext for the DataTemplate is set to the workspace collection. Thus, the TabControl can bind to the workspace collection by specifying a Binding without a Path.
The article says By relying on data binding, the Content property of a
TabItem receives a ViewModelBase-derived object to display. How ?!?
Ok, through data binding. But that is just too much to abstract away,
for me.
Again, this comes down to WPF automatically assigning the correct object as the data context of the generated item (a TabItem in this case). When an ItemsControl (such as TabControl) is bound to a collection, it generates a container (a TabItem in this case) for each item in the bound collection. The container automatically receives the data item (a workspace view model in this case) as its data context.
"But where exactly is the TabControl’s DataContext being set?" Workspaces is the DataContext and so the Itemssource for the TabControl.
<ContentControl Content="{Binding Path=Workspaces}" ContentTemplate="{StaticResource ResourceKey=WorkspacesTemplate}"/>
"How exactly is Content="{Binding Path=Workspaces}" creating a binding between ItemsSource of the TabControl and the Workspaces (the ObservableCollection of WorkspaceViewModel)?" Workspaces are the DataContext for the DataTemplate/TabControl and the ItemsSource is set to the DataContext.
<TabControl Margin="4" ItemsSource="{Binding}"
"The article says By relying on data binding, the Content property of a TabItem receives a ViewModelBase-derived object to display. How ?!?" i dont know the article, but i assume that Workspaces is a collection of object derived from ViewModelBase. every tabitem represent on item from the collection and so a viewmodelbase-derived object.

Using TemplateBinding inside DataTemplates

I'm creating a custom control, and I'm having problems binding UI elements inside a DataTemplate to the custom control's dependency properties.
There's a content control in my control that should change its content and content template according to a certain property, so I bound it this way -
<ContentControl Content="{TemplateBinding ControlMode}" ContentTemplateSelector="{StaticResource TemplateSelector}"/>
The Content Template selector is defined this way -
<ns:TemplateSelector x:Key="TemplateSelector">
<ns:TemplateSelector.Template1>
<DataTemplate>
<TreeView ItemsSource="{TemplateBinding TreeSource}"/>
</DataTemplate>
</ns:TemplateSelector.Template1>
<ns:TemplateSelector.Template2>
<DataTemplate>
<ListView ItemsSource="{TemplateBinding ListSource}"/>
</DataTemplate>
</ns:TemplateSelector.Template2>
</ns:TemplateSelector>
The problem is that the TreeView and the ListView can't be bound to their itemssource with TemplateBinding due to this error for example -
"Cannot find TreeSourceProperty on the type ContentPresenter"
I've been looking around for an answer and I found this answer that simple states that this is impossible.
How to use template binding inside data template in custom control (Silverlight)
So if this really is impossible, how else could I bind the elements inside my template to the DependencyProperties of the CustomControl?
Thanks!
In WPF you can use a binding with RelativeSource targeting the "templated" control.
e.g.
{Binding TreeSource,
RelativeSource={RelativeSource AncestorType=MyCustomControl}}
Edit: If you have a break in a tree you could possibly work around that by passing that control around, e.g.
<ControlThatOwnsPopup
Tag="{Binding RelativeSource={RelativeSource AncestorType=MyCustomControl}}">
<Popup>...
<TreeView ItemsSource="{Binding PlacementTarget.Tag.TreeSource,
RelativeSource={RelativeSource AncestorType=Popup}}"/>

DataTemplate in ResourceDictionary and Events

in my Silverlight 4 application, I have a listbox for which I created a nice DataTemplate. This DataTemplate contains some buttons, for which I want to handle events. So I assigned the event on the template:
<DataTemplate>
<Grid>
<Button x:Name="myB" Click="myB_Click" />
</Grid>
</DataTemplate>
In the UserControl that contains the listbox that uses this template I have the eventhandler that handles the myB_Click.
As long I have the template assigned directly within the listbox, everything works fine:
<ListBox ...>
<ListBox.ItemTemplate>
<DataTemplate>
...
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
But when I outsource the DataTemplate to a ResourceDirectory, I get a runtime parser-error when adding an item to the listbox
Kategorie: ParserError
Message:
Error assigning to Property
'System.Windows.Controls.Button.Click'.
Any idea, what might cause this?
Thanks in advance,
Frank
if you put it in a resource dictionary then it can't find the event handler since your resource dictionary doesn't have code behind.
either you (a) don't put that part in the ResourceDictionary and keep it in your xaml
or (b) add code behind to your resource dictionary

Correct way to do DataBinding in a WP7 UserControl

I'm building a Windows Phone 7 App. So, I'm using SilverLight 3(.7) and I'm having a bit of a problem with the UserControls that I've built. Here's how I'm doing it now:
UserControl has a DependencyProperty named Number
<UserControl x:Class="MyUserControl" x:Name="myUserControl">
<TextBlock Text="{Binding ElementName=myUserControl, Path=Number}"/>
</UserControl>
To use it I'm simply doing:
<MyUserControl Number="{Binding ElementName=MyPage, Path=SomeNumber}">
<MyUserControl Number="{Binding ElementName=MyPage, Path=SomeOtherNumber}">
This all works great, but if I add a name to one of the two instances of my control, things go wacky. For example:
<MyUserControl x:Name="SomeNumberControl"
Number="{Binding ElementName=MyPage, Path=SomeNumber}">
<MyUserControl Number="{Binding ElementName=MyPage, Path=SomeOtherNumber}">`
Then the data doesn't show up. It appears that the name given overrides the name specified in the UserControl and the bindings don't work.
So, I tried to do binding through the datacontext. And setting the DataContext to the UserControl.
so, my control became:
<UserControl x:Class="MyUserControl"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<TextBlock Text="{Binding Number}"/>
</UserControl>
With this nothing gets bound. It's like the datacontext is never set and even weirder when I put this control in another user control that I created, It appears that the parent control is now somehow bound to the MyUserControl instance.
So when I do the following:
<MyPage>
<MyUserControl Number={Binding SomeNumber}"/>
I get the error,
SomeNumber not found in type MyUserControl.
It's as if the binding for the parent control is now the instance of MyUserControl. Am I just doing something fundamentally wrong in how I'm doing bindings in my user control, or is this something strange with SilverLight 4 and WP7.
Any help on this is greatly appreciated.
If you have a DependecyProperty in your codebehind called Number you can set
DataContext = this;
That should allow you to bind directly to your Number property from TextBlock.
<TextBlock Text="{Binding Number}"/>

Can I get strongly typed bindings in WPF/XAML?

Using the MVVM-pattern you set the DataContext to a specific ViewModel. Now is there any way to tell the XAML the type of the DataContext so that it will validate my bindings?
Looking for something like the typed viewdata in ASP.NET MVC.
You can write each individual binding in a strongly-typed way:
<TextBox Text="{Binding Path=(vm:Site.Contact).(vm:Contact.Name)}" />
However this would not validate the fact that TextBox DataContext is of type ViewModel.Site (and I think this is not possible, but I may be wrong).
No, the current spec does not have strong typing in Xaml. I believe that with .Net 4.0, Xaml should be seeing the capacity for generics. With that, I would think it should be much easier to have strong typing in Xaml.
No. FrameworkElement.DatatContext is the dependency property that enables data binding is of type object.
As pointed out by others, you can specify the expected type of a DataContext for a special template called a DataTemplate. Many controls such as ItemsControl, ControlControl provide access to DataTemplates to allow you to set the visual representation's expectations of the DataContext's type.
Bryan is correct, he did not test his code.
The correct application of a typed DataTemplate looks like this:
<Window>
<Window.Resources>
<DataTemplate x:Key="TypedTemplate" DataType="{x:Type myViewModel}">
...
</DataTemplate>
</Window.Resources>
<ContentControl Content="{Binding}" ContentTemplate="{StaticResource TypedTemplate}" />
</Window>
ContentPresenter inherits directly from FrameworkElement and does not have a Template property. In addition, the Template property commonly refers to Control.Template of type ControlTemplate which is something entirely different than a DataTemplate.
I think Bryan was thinking of the ContentControl which is one of the two root control types (the other being ItemsControl). ContentControl does in fact inherit from Control. Therefore we can specify the Template property on it if we so choose.
<Window>
<Window.Resources>
<DataTemplate x:Key="TypedTemplate" DataType="{x:Type myViewModel}">
...
</DataTemplate>
<ControlTemplate x:Key="ControlSkin" TargetType="{x:Type ContentControl}">
...
</ControlTemplate>
</Window.Resources>
<ContentControl Content="{Binding}" ContentTemplate="{StaticResource TypedTemplate}" Template="{StaticResource ControlSkin}" />
</Window>
I personally declare a static PropertyPath for each property in my viewmodel the reference this using x:static as the binding path -
e.g
public class MyViewModel
{
public static PropertyPath MyPropertyPath = new PropertyPath("MyProperty");
public bool MyProperty{get; set;}
}
xaml : {Binding Path={x:Static local:MyViewModel.MyPropertyPath}}
This way all my bindings get validated on build.
Try this:
<Window>
<Window.Resources>
<DataTemplate x:Key="TypedTemplate" DataType="{x:Type myViewModel}">
...
</DataTemplate>
</Window.Resources>
<ContentPresenter Content="{Binding}" Template="{StaticResource TypedTemplate}" />
</Window>
I haven't tested this code but it should give you the idea. The content presenter will display the current DataContext which will use the DataTemplate. This isn't strongly typed in the compiler but will throw a runtime error immediately on load (in the window's InitializeComponent). You should be able to catch this easily in your testing if something breaks.

Resources