Correct way to do DataBinding in a WP7 UserControl - silverlight

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}"/>

Related

Simple ListView databinding issue

I guess there is something trivial I am overlooking here. The Window has a DataContext and everything else in the window databinds nicely. The same RecentItems is also databound to a Menu without any problems.
My XAML:
<ListView ItemsSource="{Binding Path=RecentItems}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock>◾ <Hyperlink Command="{Binding Path=Command}"><TextBlock Text="{Binding Path=Header}"/></Hyperlink></TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
WPF renders:
I should perhaps also mention the ListView is embedded inside a FlowDocument via a BlockUIContainer.
EDIT: I changed the XAML so that I use a Run inside the TextBlock instead - I can understand a nested TextBlock is not a good idea (although then I expect a runtime error at least) - but that gives me another binding error saying that I cannot bind to a readonly property - even if I change the Mode to OneWay.

How to define DataContext in my Resource File or under Window.Resource tag?

I have this binding:
<Window x:Name="_local">
<TextBox x:Name="txtVendorName" DataContext="{Binding ElementName=_this, Path=VendorObject}" Width="200" Height="50" BorderBrush="Black" Text="{Binding Path=VendorName}" />
if i have 50 textboxes and label which I need to bind, do I need to put DataContext for all??
Is there a way I can centralize this DataContext and only define Text/Content for my objects?
I don't want to define DatContext to my Grid. So how can I define DataContext in my?
<Window.Resources></Window.Resources>
If I am guessing your actual requirement right, then what you want is all 50 labels and textblocks should be bound to the data context which is VendorObject from _this object. Although I couldnt really guess what _this object is.
Well DataContext is an Inherited Dependency Property (IDP) what they mean is when a visual parent is set with a data context its data context is acquired by all the children below that visual.
So if you put some Panel in your window and set its data context once with your binding DataContext="{Binding ElementName=_this, Path=VendorObject}" and then put all those 50-100 textblocks and labels under that panel then they will automatically acquire that vendor-object as the data context.
So
<StackPanel DataContext="{Binding ElementName=_this, Path=VendorObject}">
<TextBlock Text="{Binding Path=VendorName}" />
<TextBlock Text="{Binding Path=VendorId}" />
<TextBlock Text="{Binding Path=VendorLocation}" />
.... <!--Put all the 50 UI items here to use same data context -->
</StackPanel>
I hope I guessed correctly of what you need.
Now answering you next question,
So how can I define DataContext in my?
<Window.Resources></Window.Resources>
You cant. But you can create an instance of VendorObject class and give it a resource Key, but thats simply a bad design.

ElementName not working when assigning DataTemplate from code behind?

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

What exactly does WPF Data Binding's "RelativeSource FindAncestor" do?

I am currently working within a WPF user control (the root element of my XAML file is "UserControl"), which I know is being hosted inside a Window. How can I access a property of the Window using data binding?
Does anyone know why simply
<Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type Window}}" Path="..." />
does not work? The error message I get is:
System.Windows.Data Warning: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Window', AncestorLevel='1''.
Edit: I ended up using a variation on ArsenMkrt's approach, so have accepted his answer. However, I am still interested in finding out why FindAncestor does not "just work".
The best way is to give a name to UserControl
Create dependency property MyProperty in UserControl with two way binding and bind it in main Window, than bind in UserControl like this
<UserControl x:Name = "myControl">
<Label Content={Binding ElementName= myControl, Path=MyProperty}/>
</UserControl>
If you're trying to 'escape' from an ItemsControl or DataGridView to get to a Window you may be finding that AncestorType of x:Type Window doesn't work. Or at least doesn't seem to...
If this is the case you're probably running Blend or Visual Studio and expecting the data to be visible at design time - which it won't because VS + Blend both create their own instances that aren't really Windows. It will work at runtime just fine, but not during design mode.
There's a couple things you can do:
Wrap in a UserControl
Here's an alternative solution I've come up with. It has one advantage in that you're not referencing a UserControl or Window directly, so if you change the parent container your code won't break.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:views="clr-namespace:MyWPFApplication.Views"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="MyWPFApplication.Views.UPCLabelPrinterWindow"
mc:Ignorable="d"
x:Name="LayoutRoot"
Title="UPCLabelPrinterWindow">
<views:DataContextWrapper>
<DockPanel>
...
</DockPanel>
</views:DataContextWrapper>
Where DataContextWrapper is just a Grid
namespace MyWPFApplication.Views {
public class DataContextWrapper : Grid
{
}
}
Then when you bind you do this :
<TextBlock Text="{Binding="{Binding DataContext.SomeText,
RelativeSource={RelativeSource AncestorType={x:Type views:DataContextWrapper},
Mode=FindAncestor}}" />
Note: if you want to bind to a property ON Window itself it's trickier and you should probably bind via a dependency property or something like that. But if you are using MVVM then this is one solution I found.
I Think You Should SET Mode="OneWayToSource" Like this:
<TextBox Text="{Binding RelativeSource={RelativeSource FindAncestor ,AncestorType={x:Type Grid}},Path=BackGround , Mode=OneWayToSource , UpdateSourceTrigger = PropertyChanged}" />
If you are using a view model as your Window's DataContext and the property you need to bind to is from that view model then you should prefix the path with DataContext.MyPropertyPath, something like this:
<TextBox Text="{Binding DataContext.MyProperty, RelativeSource={RelativeSource AncestorType={x:Type Window}, Mode=FindAncestor}}"/>
this translates as "Find me an ancestor window and then look in it's data context for MyProperty"

Silverlight Binding

I've a Silverlight page using a MVVM behind it to handle all the data bits going on.
The data context is set for the page using:
DataContext="{Binding AddNewClientViewModel, Source={StaticResource ServiceLocator}}"
ServiceLocator being a service that allows me to create and inject the appropriate VM using an IoC container.
This all works fine.
Now I have a DataForm like so:
<df:DataForm CurrentItem="{Binding NewClient}" AutoGenerateFields="False" >
<df:DataForm.NewItemTemplate>
<DataTemplate>
<StackPanel>
<df:DataField>
<TextBox Text="{Binding ClientName}" />
</df:DataField>
<df:DataField>
<TextBox Text="{Binding Property_on_the_VM_not_on_NewClient}" />
</df:DataField>
</StackPanel>
</DataTemplate>
</df:DataForm.NewItemTemplate>
</df:DataForm>
OK, so this dataform binds to the NewClient property on my ViewModel. The first DataField binds to the NewClient.ClientName. The second DataField I'd like to bind to a property that hangs of the root ViewModel.
I know there is the 'Source' parameter that you can pass in when binding, if I had a static resource of the VM or similar I could point it to that, but I don't. How can I link this binding up with property on the parent VM?
Edit
After a post by Jobi below, I've tried the following:
<TextBox DataContext="{Binding DataContext, ElementName=root}" Text="{Binding MyProperty}" />
And my top level control:
x:Name="root"
DataContext="{Binding AddNewClientViewModel, Source={StaticResource ServiceLocator}}"
No dice with getting this to work...
What you need is a DataContextProxy which was created by Dan Wahlin. The problem is that once you get into the data form, you have a new data context. There is no easy way to reach back up to the view's data context. The data context proxy allows you to easily do this and I've used it quite a bit found it works great.
On the second TextBox you can do an ElementName binding to the root element where the Parent VM has already DataContext to.
<TextBox DataContent="{Binding DataContext, ElementName=rootLevelControl}" Text="{Binding property}"

Resources