Basic WPF databinding question - wpf

I have another WPF databinding question... one that I haven't found an answer to anywhere, and this surprises me since it seems like it is very basic.
Essentially, I have a string in code behind that I would like to establish a two-way binding with with a textbox in my GUI. I thought it was a simple matter of creating a DependencyProperty in the code behind, and then tying it to the TextBox via a Source binding. The problem is, I can't get one or both parts right.
Here is my DependencyProperty definition from the code behind:
public static readonly DependencyProperty FilePathProperty = DependencyProperty.Register( "FilePath", typeof(string), typeof(Window1));
public string FilePath
{
get { return (string)GetValue(FilePathProperty); }
set { SetValue( FilePathProperty, value); }
}
And here is my XAML:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ReportingInterface Test Application" Height="300" Width="536">
<Menu DockPanel.Dock="Top">
<MenuItem Name="menu_plugins" Header="File">
<MenuItem Header="Open">
<StackPanel Orientation="Horizontal">
<Label>File location:</Label>
<TextBox Name="text_filepath" Width="100" Text="{Binding Source=FilePath, Path=FilePath, Mode=TwoWay}"></TextBox>
<Button Margin="3" Width="20">...</Button>
</StackPanel>
</MenuItem>
</MenuItem>
</Menu>
The part I know is obviously wrong is the Binding part... I hate to waste people's time here with this question, but I honestly have come up short with every search (but now at least this request will populate subsequent google searches). :)
Thank you!

When you defined a binding in XAML, it binds to whatever is set as the DataContext for the object (or it's parent).
This typically means you'd set the DataContext of the Window to some class, and then the binding will work:
<TextBox Name="text_filepath" Width="100" Text="{Binding Path=FilePath, Mode=TwoWay}" />
You can fix this by adding, in the Window's constructor:
this.DataContext = this;
That will make the binding work against the window itself.
Alternatively, you can setup the binding to bind against a specific source object. If, in this case, you wanted to be able to use something else as the DataContext, but still want to bind to a Dependency Property defined in your Window, you could do:
<TextBox Name="text_filepath" Width="100" Text="{Binding Path=FilePath, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}"></TextBox>
This works by telling the binding to find the first ancestor of type "Window", and bind it the "FilePath" property on that object.

For what it's worth, I would recommend looking into the M-V-VM pattern (Model, View, ViewModel)- essentially, what you do is have this class that serves as the DataContext for your XAML, and all your fun exposed properties/commands/what have you are exposed as public members of that class (called a ViewModel).
Here's a good overview webcast:
MVVM video
And here's another from MSDN mag:
http://msdn.microsoft.com/en-us/magazine/dd419663.aspx

Related

Understanding dependancy objects

I'm totally lost with dependancy objects and binding. I often get things working without understanding why and how, this question is about knowing what should be happening.
I have a tiny user control with the following XAML
<Grid>
<Image Source="{Binding Icon}"></Image>
<TextBlock Text="{Binding Title}"></TextBlock>
</Grid>
My code behind has the following
public static readonly DependencyProperty IconProperty =
DependencyProperty.Register("Icon", typeof(Image), typeof(MenuItem));
public Image Icon
{
get { return (Image)GetValue(IconProperty); }
set { SetValue(IconProperty, value); }
}
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(String), typeof(MenuItem));
public string Title
{
get { return (string)GetValue(IconProperty); }
set { SetValue(IconProperty, value); }
}
My MainWindow is empty, other than a reference to this control and to the ResourceDictionary. In the MainWindow code behind, I set the DataContext in the constructor.
<Window x:Class="AppUi.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:loc="clr-namespace:AppUi.Control"
Title="">
//set up to Resource Dictionary - all binding and styling works fine :)
<loc:MenuItem Icon="{Binding MailIcon}" Title="{Binding MailTitle}"></loc:MenuItem>
In the ModelView for the MainWindow, I have the following 2 properties
private Image_mailIcon;
public Image MailIcon{
//inotifyproperty implementation
}
private string _mailTitle;
public string MailTitle{
//inotifyproperty implementation
}
My question is, in the UserControl, how do I do the binding? Since it's a user control within a MainWindow, and the MainWindow already has a datacontext, I think the UserControl will inherit the DataContext from the parent (From what I have read).
So, in my UserControl XAML, should I be binding to the MainWindow's Code Behind properties OR to the ViewModel properties?
In other words, should my UserControl be
<Grid>
<Image Source="{Binding MailIcon}"></Image>
<TextBlock Text="{Binding MailTitle}"></TextBlock>
</Grid>
OR
<Grid>
<Image Source="{Binding Icon}"></Image>
<TextBlock Text="{Binding Title}"></TextBlock>
</Grid>
Or, because I'm using a DataContext and the UserControl inherits, do I even need the Dependancy Properties at all?
You normally don't want to overwrite DataContext passed through visual tree so you can use either ElementName or RelativeSource binding inside UserControl to change binding context. The easiest way to achive this is give UserControl some name and use it ElementName binding
<UserControl ... x:Name="myUserControl">
<!-- ... -->
<Grid>
<Image Source="{Binding Icon, ElementName=myUserControl}"/>
<TextBlock Text="{Binding Title, ElementName=myUserControl}"/>
</Grid>
<!-- ... -->
</UserControl>
This way binding is DataContext independent. You can also create UserControl with assumption it will always work with only specific type of DataContext and then you just use Path from that view model type but then DataContext of that UserControl must always be of the view model it's designed for (mostly inherited through visual tree)
<UserControl ...>
<!-- ... -->
<Grid>
<Image Source="{Binding MailIcon}"/>
<TextBlock Text="{Binding MailTitle}"/>
</Grid>
<!-- ... -->
</UserControl>
I would also change type of Icon property from Image to ImageSource for example. You already have Image control inside your UserControl and you just want to bind its Source
in the UserControl, how do I do the binding? ... the UserControl will inherit the DataContext from the parent
That is correct, the UserControl will inherit the DataContext from the parent Window. Therefore you can data bind from the UserControl directly to the parent Window.DataContext. Please note that you would bind to whatever object has been set as the DataContext, regardless of whether that was the code behind or a separate view model class.
However, you don't have to data bind to the parent's DataContext object in this situation... you have other options. You could data bind to your own UserControl DependencyPropertys using a RelativeSource Binding like this:
<TextBlock Text="{Binding Title, RelativeSource={RelativeSource
AncestorType={x:Type YourPrefix:YourUserControl}}}" />
You could also name your UserControl and reference its properties like this:
<TextBlock Text="{Binding Title, ElementName=YourUserControlName}" />
While this example seems to be more concise, don't overlook the first example, as RelativeSource is a useful and powerful friend to have.
should I be binding to the MainWindow's Code Behind properties OR to the ViewModel properties?
That's your choice... what do you want or need to data bind to? you just need to know that a direct data binding will use the auto set DataContext value, so if you don't want to use that, then you can just specify a different data source for the Binding as shown above.
Finally, regarding the need to use DependencyPropertys... you only need to declare them if you are developing a UserControl that needs to provide data binding abilities.

Bind user control dependency properties in MVVM style Windows Phone app

I'm having some issues with binding some custom controls in a Windows Phone app right now. Usually this is never an issue but apparently my mind can't comprehend this today.
So I'm doing an MVVM style setup which is good. I have my page with a view and also a viewmodel. Now on a WebClient callback I assign the dataContext of my view to the list of models in my ViewModel, nice and simple thus far...now in my view I created a ListBox with a custom control in the datatemplate which is basically a cell in the list. I once again set my user controls dataContext to binding, and binding all the models values to the regular UI elements works no problem.
Here's a sample:
<Grid Grid.Column="0">
<Image Source="{Binding SmallPath}" VerticalAlignment="Top"/>
</Grid>
<Grid Grid.Column="1">
<StackPanel Margin="12,0,0,0">
<TextBlock x:Name="MemberId_TextBlock" Text="{Binding MemberId}" FontSize="28"
Margin="0,-8,0,0"
Foreground="{StaticResource PhoneForegroundBrush}"/>
<StackPanel Orientation="Horizontal" Margin="0,-11,0,0">
<TextBlock Text="{Binding DaysReported}" FontSize="42"
Margin="0,0,0,0"
Foreground="{StaticResource PhoneAccentBrush}"/>
<TextBlock Text="days" FontSize="24"
Margin="3,19,0,0"
Foreground="{StaticResource PhoneSubtleBrush}"/>
</StackPanel>
</StackPanel>
</Grid>
That's in my user control, and here's the the view where the usercontrol is housed:
<Grid x:Name="LayoutRoot" Background="Transparent">
<ListBox Name="TopSpotter_ListBox" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<!--<TextBlock Text="{Binding MemberId}"/>-->
<controls:TopSpotterItemControl DataContext="{Binding}"/>
<Grid Height="18"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
Now this is good enough but what I want to do in my view is set data from my model like Booleans that determine whether or not I should show certain Grids etc. So if I try to set a dependency property explicitly in my control it fires and will run logic in the Getter/Setters for instance. HOWEVER if I try to set these custom objects from a binding source it won't actually set.
Here's what works:
<controls:TopSpotterItemControl ChampVisibility="True">
This way will trigger the ChampVisibility property and then in the code behind of the user control I can set visibilities.
Here's what fails but I want to work:
<controls:TopSpotterItemControl ChampVisibility="{Binding IsChamp">
In addition I can still set the DataContext to {Binding} and the result will be unchanged.
In this scenario IsChamp is part of my model that I would like to bind to this user control which I guess comes from the dataContext being set on the view from the viewModel. I'm not sure what I can do to get this so the bindings work etc. without having to set custom properties.
Finally, here's my user control:
public partial class TopSpotterItemControl : UserControl
{
public string MemberId
{
get
{
return this.MemberId_TextBlock.Text;
}
set
{
this.MemberId_TextBlock.Text = value;
}
}
public bool ChampVisibility {
set
{
if (value)
{
this.Champ_Grid.Visibility = System.Windows.Visibility.Visible;
}
}
}
public static readonly DependencyProperty MemberNameProperty =
DependencyProperty.Register("MemberId", typeof(string), typeof(TopSpotterItemControl), new PropertyMetadata(null));
public static readonly DependencyProperty ChampVisibilityProperty =
DependencyProperty.Register("ChampVisibility", typeof(bool), typeof(TopSpotterItemControl), new PropertyMetadata(null));
public TopSpotterItemControl()
{
InitializeComponent();
}
}
Bit long winded and I hope I made things on the issue clear. My one major hang up so far, and I'd like to abstract as much control as I can to the user control via dependency properties explicitly set in xaml, rather than setting up binding in its xaml that depend on the knowledge of a model. Thanks!
Your DependencyProperty is badly formed. (I also don't see Champ_Grid defined in your class or XAML, but I assume that is an ommission)
Setting ChampVisibility = true in code works because it is unrelated to the DependencyProperty.
You can tell easily because the default value for your DP is invalid. It will compile, but the instance constructor will through an exception if it is ever invoked.
new PropertyMetadata(null)
bool = null = exception
If you call GetValue(TopSpotterItemControl.ChampVisibilityProperty) from somewhere you can confirm all of the above.
You should make changes to instance fields in the property changed handler and declare the property like the following, it will work:
Note that the property has to change (not just be set) for the event to be raised.
public bool ChampVisibility
{
get { return (bool)GetValue(ChampVisibilityProperty); }
set { SetValue(ChampVisibilityProperty, value); }
}
public static readonly DependencyProperty ChampVisibilityProperty =
DependencyProperty.Register("ChampVisibility ", typeof(bool), typeof(TopSpotterItemControl), new PropertyMetadata(true, (s, e) =>
{
TopSpotterItemControl instance = s as TopSpotterItemControl;
instance.Champ_Grid.Visibility = instance.ChampVisibility ? System.Windows.Visibility.Visible : System.Windows.Visibility.Collapsed;
}));
Incidentally, your MemberId DependencyProperty is also completely wrong and cannot work.
Note:
The Binding on your TextBox works, because it is binding to the DataContext (your model), so it probably shows the right value.
The Dependency property in your UserControl will never be set though.
Use the propdp code-snippet in Visual Studio so you dont have to concern yourself with the complexities of Dependency Property declaration.
Also check this out for more info about Dependency Properties

Binding Telerik RadRichTextBox through MVVM

I can't seem to pick my way through Telerik's terrible documentation for binding through an MVVM situation. We've got the standard view, and view model. The viewmodel exposes a property, 'Body' that is supposed to represent what the user is typing. Here are the relevant lines of xaml:
<telerik:DocxDataProvider
x:Name="DocxProvider"
RichTextBox="{Binding ElementName=editor}"
Docx="{Binding Body, Mode=TwoWay,
UpdateSourceTrigger=LostFocus}" />
<telerik:RadRichTextBox
Grid.Row="1"
x:Name="editor"
Margin="0"
AllowDrop="True"
ShowComments="False"
MinWidth="800"
MinHeight="300"
MaxWidth="1024"
MaxHeight="1200"
HorizontalAlignment="Left"
HorizontalContentAlignment="Left"
Width="790"
Padding="20,20,0,20"
TabIndex="10"
Cursor="IBeam"
IsSpellCheckingEnabled="True" >
</telerik:RadRichTextBox>
But when I set a break point on Body, it never gets hit. What property do I need to bind to so that my view model actually gets the content of the text box?
Have you tried binding to Rtf property of telerik:RadRichTextBox; another thing can be the ordering of your elements, can you try declaring <telerik:RadRichTextBox before <telerik:DocxDataProvider
Also have a look at this thread on telerik forum -
http://www.telerik.com/community/forums/wpf/richtextbox/binding-document.aspx
Did you remember to set the datacontext in the constructor of the xaml.cs file:
public View(ViewModel viewModel)
{
InitializeComponent();
DataContext = viewModel;
}
This is how you hook up the viewmodel to the view.

Binding from items of an UserControl with custom collection property

This question is a "sequel" to this question (I have applied the answer, but it still won't work).
I'm trying to create an extended ToolBar control for a modular application, which can load its items from multiple data sources (but that is not the issue I'm trying to solve right now, now I want it to work when used as regular ToolBar found in WPF).
In short: I want the ToolBar's items to be able to bind to the tb:ToolBar's parents.
I have following XAML code:
<Window Name="myWindow" DataContext="{Binding ElementName=myWindow}" >
<DockPanel>
<tb:ToolBar Name="toolbar" DockPanel.Dock="Top" DataContext="{Binding ElementName=myWindow}>
<tb:ToolBar.Items>
<tb:ToolBarControl Priority="-3">
<tb:ToolBarControl.Content>
<StackPanel Orientation="Horizontal">
<TextBlock>Maps:</TextBlock>
<ComboBox ItemsSource="{Binding SomeProperty, ElementName=myWindow}">
Some info about the types:
tb:ToolBar is an UserControl with dependency property Items of type FreezableCollection<ToolBarControl>.
tb:ToolBarControl is an UserControl with template pretty much identical to ContentControl's template.
The problem is that the binding in the ComboBox fails (with the usual "Cannot find source for binding with reference"), because its DataContext is null.
Why?
EDIT: The core of the question is "Why is the DataContext not inherited?", without it, the bindings can't work.
EDIT2:
Here is XAML for the tb:ToolBar:
<UserControl ... Name="toolBarControl">
<ToolBarTray>
<ToolBar ItemsSource="{Binding Items, ElementName=toolBarControl}" Name="toolBar" ToolBarTray.IsLocked="True" VerticalAlignment="Top" Height="26">
EDIT 3:
I posted an example of what works and what doesn't: http://pastebin.com/Tyt1Xtvg
Thanks for your answers.
I personally don't like the idea of setting DataContext in controls. I think doing this will somehow break the data context inheritance. Please take a look at this post. I think Simon explained it pretty well.
At least, try removing
DataContext="{Binding ElementName=myWindow}"
from
<tb:ToolBar Name="toolbar" DockPanel.Dock="Top" DataContext="{Binding ElementName=myWindow}>
and see how it goes.
UPDATE
Actually, keep all your existing code (ignore my previous suggestion for a moment), just change
<ComboBox ItemsSource="{Binding SomeProperty, ElementName=myWindow}">
to
<ComboBox ItemsSource="{Binding DataContext.SomeProperty}">
and see if it works.
I think because of the way you structure your controls, the ComboBox is at the same level/scope as the tb:ToolBarControl and the tb:ToolBar. That means they all share the same DataContext, so you don't really need any ElementName binding or RelativeSource binding to try to find its parent/ancestor.
If you remove DataContext="{Binding ElementName=myWindow} from the tb:ToolBar, you can even get rid of the prefix DataContext in the binding. And this is really all you need.
<ComboBox ItemsSource="{Binding SomeProperty}">
UPDATE 2 to answer your Edit 3
This is because your Items collection in your tb:ToolBar usercontrol is just a property. It's not in the logical and visual tree, and I believe ElementName binding uses logical tree.
That's why it is not working.
Add to logical tree
I think to add the Items into the logical tree you need to do two things.
First you need to override the LogicalChildren in your tb:ToolBar usercontrol.
protected override System.Collections.IEnumerator LogicalChildren
{
get
{
if (Items.Count == 0)
{
yield break;
}
foreach (var item in Items)
{
yield return item;
}
}
}
Then whenever you added a new tb:ToolBarControl you need to call
AddLogicalChild(item);
Give it a shot.
This WORKS...
After playing around with it a little bit, I think what I showed you above isn't enough. You will also need to add these ToolBarControls to your main window's name scope to enable ElementName binding. I assume this is how you defined your Items dependency property.
public static DependencyProperty ItemsProperty =
DependencyProperty.Register("Items",
typeof(ToolBarControlCollection),
typeof(ToolBar),
new FrameworkPropertyMetadata(new ToolBarControlCollection(), Callback));
In the callback, it is where you add it to the name scope.
private static void Callback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var toolbar = (ToolBar)d;
var items = toolbar.Items;
foreach (var item in items)
{
// the panel that contains your ToolBar usercontrol, in the code that you provided it is a DockPanel
var panel = (Panel)toolbar.Parent;
// your main window
var window = panel.Parent;
// add this ToolBarControl to the main window's name scope
NameScope.SetNameScope(item, NameScope.GetNameScope(window));
// ** not needed if you only want ElementName binding **
// this enables bubbling (navigating up) in the visual tree
//toolbar.AddLogicalChild(item);
}
}
Also if you want property inheritance, you will need
// ** not needed if you only want ElementName binding **
// this enables tunneling (navigating down) in the visual tree, e.g. property inheritance
//protected override System.Collections.IEnumerator LogicalChildren
//{
// get
// {
// if (Items.Count == 0)
// {
// yield break;
// }
// foreach (var item in Items)
// {
// yield return item;
// }
// }
//}
I have tested the code and it works fine.
I took the pieces of Xaml that you posted and tried to reproduce your problem.
The DataContext seems to be inheriting just fine from what I can tell. However, ElementName Bindings fail and I think this has to do with the fact that even though you add the ComboBox in the Window, it ends up in a different scope. (It is first added to the Items property of the custom ToolBar and is then populated to the framework ToolBar with a Binding)
A RelativeSource Binding instead of an ElementName Binding seems to be working fine.
But if you really want to use the name of the control in the Binding, then you could check out Dr.WPF's excellent ObjectReference implementation
It would look something like this
<Window ...
tb:ObjectReference.Declaration="{tb:ObjectReference myWindow}">
<!--...-->
<ComboBox ItemsSource="{Binding Path=SomeProperty,
Source={tb:ObjectReference myWindow}}"
I uploaded a small sample project where both RelativeSource and ObjectReference are succesfully used here: https://www.dropbox.com/s/tx5vdqlm8mywgzw/ToolBarTest.zip?dl=0
The custom ToolBar part as I approximated it looks like this in the Window.
ElementName Binding fails but RelativeSource and ObjectReference Bindings work
<Window ...
Name="myWindow"
tb:ObjectReference.Declaration="{tb:ObjectReference myWindow}">
<!--...-->
<tb:ToolBar x:Name="toolbar"
DockPanel.Dock="Top"
DataContext="{Binding ElementName=myWindow}">
<tb:ToolBar.Items>
<tb:ContentControlCollection>
<ContentControl>
<ContentControl.Content>
<StackPanel Orientation="Horizontal">
<TextBlock>Maps:</TextBlock>
<ComboBox ItemsSource="{Binding Path=StringList,
ElementName=myWindow}"
SelectedIndex="0"/>
<ComboBox ItemsSource="{Binding Path=StringList,
Source={tb:ObjectReference myWindow}}"
SelectedIndex="0"/>
<ComboBox ItemsSource="{Binding Path=StringList,
RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
SelectedIndex="0"/>
</StackPanel>
</ContentControl.Content>
</ContentControl>
</tb:ContentControlCollection>
</tb:ToolBar.Items>
</tb:ToolBar>
Often if there is no DataContext then ElementName will not work either. One thing which you can try if the situation allows it is using x:Reference.
For that you need to move the bound control into the resources of the referenced control, change the binding and use StaticResource in the place where it was, e.g.
<Window Name="myWindow" DataContext="{Binding ElementName=myWindow}" >
<Window.Resources>
<ComboBox x:Key="cb"
ItemsSource="{Binding SomeProperty,
Source={x:Reference myWindow}}"/>
</Window.Resources>
<DockPanel>
<tb:ToolBar Name="toolbar" DockPanel.Dock="Top" DataContext="{Binding ElementName=myWindow}>
<tb:ToolBar.Items>
<tb:ToolBarControl Priority="-3">
<tb:ToolBarControl.Content>
<StackPanel Orientation="Horizontal">
<TextBlock>Maps:</TextBlock>
<StaticResource ResourceKey="cb"/>
The proper answer is probably to add everything to the logical tree as mentioned in previous answers, but the following code has proved to be convenient for me. I can't post all the code I have, but...
Write your own Binding MarkupExtension that gets you back to the root element of your XAML file. This code was not compiled as I hacked up my real code to post this.
[MarkupExtensionReturnType(typeof(object))]
public class RootBindingExtension : MarkupExtension
{
public string Path { get; set; }
public RootElementBinding(string path)
{
Path = path;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
IRootObjectProvider rootObjectProvider =
(IRootObjectProvider)serviceProvider.GetService(typeof(IRootObjectProvider));
Binding binding = new Binding(this.Path);
binding.Source = rootObjectProvider.RootObject;
// Return raw binding if we are in a non-DP object, like a Style
if (service.TargetObject is DependencyObject == false)
return binding;
// Otherwise, return what a normal binding would
object providedValue = binding.ProvideValue(serviceProvider);
return providedValue;
}
}
Usage:
<ComboBox ItemsSource={myBindings:RootBinding DataContext.SomeProperty} />

WPF databinding with a user control

I have a wpf user control, which exposes a single custom dependency property. Inside the user control, a textblock binds to the value of the dp. This databinding works in all scenarios except when the data source is an object.
The minimal code necessary to reproduce this is:
this is the main part of the user control
<StackPanel Orientation="Horizontal">
<TextBlock Text="**SimpleUC** UCValue: "/>
<TextBlock Text="{Binding UCValue}"/>
</StackPanel>
and the user control code behind:
public SimpleUC()
{
InitializeComponent();
this.DataContext = this;
}
public string UCValue
{
get { return (string)GetValue(UCValueProperty); }
set { SetValue(UCValueProperty, value); }
}
public static readonly DependencyProperty UCValueProperty =
DependencyProperty.Register("UCValue", typeof(string), typeof(SimpleUC), new UIPropertyMetadata("value not set"));
this is the test window. I imported my project xml namespace as "custom"
<Window.Resources>
<Style TargetType="{x:Type StackPanel}">
<Setter Property="Margin" Value="20"/>
</Style>
</Window.Resources>
<StackPanel>
<StackPanel>
<TextBlock Text="This fails to bind:"/>
<custom:SimpleUC UCValue="{Binding SomeData}"/>
</StackPanel>
<StackPanel>
<TextBlock>The same binding on a regular control like Label</TextBlock>
<Label Content="{Binding SomeData}"/>
</StackPanel>
<Slider x:Name="sld" />
<StackPanel>
<TextBlock>However, binding the UC to another element value, like a slider works</TextBlock>
<custom:SimpleUC UCValue="{Binding ElementName=sld,Path=Value}"/>
</StackPanel>
</StackPanel>
and the test window code behind is:
public TestWindow()
{
InitializeComponent();
this.DataContext = this;
}
//property to bind to
public string SomeData { get { return "Hello S.O."; } }
When I turn on the diagnostic tracing on the TestWindow, it spits out the error "BindingExpression path error:
'SomeData' property not found on 'object' ''SimpleUC' (Name='')' ... "
The binding expression is the same as the one I used in the neighboring label and it worked fine. This behavior seems really bizarre to me. Can anyone shed some light?
You set DataContext of your SimpleUC to itself here
public SimpleUC()
{
InitializeComponent();
this.DataContext = this; // wrong way!
}
so when you use binding here
<custom:SimpleUC UCValue="{Binding SomeData}"/>
it searches property SomeData in control's data context which is set to this object because code in SimpleUC constructor overrides value of DataContext and it is not set to TestWindow object anymore as you expected. That's why your solution works - it doesn't affect DataContext which is inherited from window. Also you can keep this.DataContext = this; but set element where to search property explicitly like this (skipped irrelevant)
<Window ... Name="wnd1">
<custom:SimpleUC UCValue="{Binding SomeData, ElementName=wnd1}"/>
...
But my oppinion is that your variant from the answer looks more convenient to me, setting data context to this is not very good practice.
Hope it helps.
If you must use a UserControl, your
<TextBlock
Text="{Binding RelativeSource={RelativeSource Self},
Path=Parent.Parent.UCValue}"
/>
is an ok way to do it and
<TextBlock
Text="{Binding UCValue,
RelativeSource={RelativeSource FindAncestor,custom:SimpleUC,1}}"
/>
is better because you don't rely on the control hierarchy and possible instantiation order issues.
However I would recommend for this kind of situation that you use "custom controls" instead of "user controls". They take a little bit of getting used to, but they are much more powerful because their XAML is the template itself which means you can use TemplateBinding and {RelativeSource TemplatedParent}.
In any case, DataContext = this; is definitely to be avoided.

Resources