Binding to an element within a UserControl - wpf

Say I have a user control like the one below, how would I bind something to the ActualWidth of the "G1" grid from outside of the control?
<UserControl x:Class="Blah">
<WrapPanel>
<Grid x:Name="G1">
...
</Grid>
<Grid>
...
</Grid>
</WrapPanel>
</UserControl>

If you mean with outside the control, not as Content of the control, you can use ElementName in the Binding like so:
{Binding ElementName=G1, Path=ActualWidth}
If you mean outside the control in another Xaml file, then you can try to use the Path property if your control is in the scope of the other control:
{Binding ElementName=ParentControl, Path=G1.ActualWidth}
However I would advise against this design, because you may change the name of G1 one day, and you would never know of any bindings that might break.

If you want to bind to an external control where you use this user control, declare a DependencyProperty at your UserControl code behind and then Bind G1 to that property.
And bind the external control's property to the UserControl's DependencyProperty.
It is like a 2 level of indirection.

Related

WPF- Binding properties in a DataTemplate

I'm building a window with a set of rows that share the same layout, but their contents should be different, eg:
| (Label Content:)"Name1" | (Textbox Text)"SomeText" |
| (Label Content:)"Name5" | (Textbox Text)"OtherText" |
I've defined a DataTemplate which basically holds a Grid specifying the size of each column, holds all the elements it requires (a few labels, textboxes, etc.) and sets their common properties.
<UserControl.Resources>
<DataTemplate x:Key="AxisRangeEntry" x:Shared="False">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="50" />
....
</Grid.ColumnDefinitions>
<Label x:Name="MyLabel" Grid.Column="0" HorizontalAlignment="Left" VerticalAlignment="Center">
...
<TextBox x:Name="MyTextbox" Grid.Column="2" Width="110" HorizontalContentAlignment="Right" />
...
</Grid>
</DataTemplate>
</UserControl.Resources>
Then in my window I start adding the data template as ContentControls in a stack panel:
<ContentControl ContentTemplate="{StaticResource AxisRangeEntry}" />
<ContentControl ContentTemplate="{StaticResource AxisRangeEntry}" />
....
I'm struggling to figure out how I can define certain properties of controls inside the DataTemplate to be bindable to, and bind them to a static value/external property when I start defining the ContentControls. Effectively each ContentControl would need to be able to define things like it's MyLabel content and MyTextbox text.
I've previously created CustomControls, which had DependencyProperties on them, which I could then bind to when adding them on another window. With a DataTemplate however I'm not sure how I would define these fields as bindable and bind to them when including a new version of the template.
Any help would be appreciated.
From what it sounds like, you are not using the MVVM pattern.
For your situation, I'd recommend using MVVM -- take a look at this article for a quick intro for something that would fit your case (ItemsControl with an ItemTemplate)
What you would do is create an ObservableObject to represent each row, and then bind the collection of ObservableObjects to an ItemsControl's ItemsSource, with the ItemTemplate set to the DataTemplate you created. In the DataTemplate, you would specify each binding to the property on the ObservableObject's row, and WPF would bind to the correct instance for each row.
http://www.wpf-tutorial.com/list-controls/itemscontrol/
Either way, DataTemplates are primarily used for templating a certain data-type. If you really need to implement the view in this way, a custom UserControl with dependency properties would be the way to go.
You present a dynamic nature of items to be bound, so this answer will attempt to provide guidance within the parameter's set.
...[to] define certain properties of
controls inside the DataTemplate to be bindable to,
Within a template the binding will default to the parents data context. Simply saying {Binding} will default to that item in the data context. If the bound item has a specific property then use {Binding MyPropertyName}. Just verify that the parent, or its ancestors have a valid data context.
Think of data templates in its final location, as if you had hard coded it there. It will behave the same....
and bind them to a static value/external property when I start defining the
ContentControls.
Since this sounds like it is in a custom control, the datacontext will be the ultimate consumer's datacontext and most likely the datacontext will be worthless.
If it is on a custom control, then use named binding and bind it to a property on the control. For example the control's name, in XAML, is given the name "MyControl" (x:Name="MyControl")and in the template binding, one can path directly to it such as
{Binding MyCustomControlDependencyProperty, ElementName=MyControl}
created CustomControls, which had Dependency properties
With the above rules one can still, and should IMHO, use dependency properties of the custom control to pass on the information from the consumer to the the datatemplate which will use it dynamically..

Binding UI-element to UI-element like Textbox to Textbox

I have UI-elements like a textbox and I want to bind them to a ViewModel. I need to access many properties of the textbox like the text- or the IsEnabled-property.
Is it possible that I bind the textbox directly to another textbox in the ViewModel with all their properties instead of binding every single property to properties?
Yes, using an ElementName, but you still bind all properties though.
<!-- Bound to ViewModel -->
<TextBox Name="tbOne" IsEnabled="{Binding OneIsEnabled}" Text={Binding TextOne}/>
<TextBox Name="tbTwo" IsEnabled="{Binding ElementName=tbOne, Path=IsEnabled}" Text={Binding ElementName=tbOne, Path=Text}/>
There is no built-in way to bind all dependency properties of the TextBox to another. Personally, I would prefer binding directly to the ViewModel.
An alternative solution would be to create a UserControl that internally clones the TextBox with all bindings:
<CloneControl Target="{Binding ElementName=tbOne}"/>
Here, the ControlControl would inspect the target, and have code that create a new TextBox, and sets the bindings in code. This is only useful if you are doing this very often, and there is a slightly performance price to pay, as you are adding another level of controls to the UI tree.

Why do I need to use a lookless control instead of a UserControl when I have swappable sub-parts

I have a WPF application with some custom controls (defined inside the same project) that have swappable sub parts. As a basic example, lets says I have some Xaml like:
<Border Background="White" CornerRadius="9">
<ContentPresenter/>
</Border>
Which is used for my class derived from ContentControl, lets call it MrWhiteControl
If I make MrWhiteControl a xaml and code-behind file pair (so it has an InitializeComponent() call in the ctor), then whatever I place in the Content property has lots of problems e.g. the DataContext won't inherit properly, and using ElementName in bindings won't work.
However, if I use a lookless control - so a MrWhiteControl.cs file with the style defined in Themes/Generic.xaml (I don't care about theming support in this app) - then everything works fine.
What I want to know is why this is the case. What's going on behind the scenes that means the lookless control works fine, but that the xaml with code-behind doesn't work properly?
I've created an example project you can clone from GitHub. If you run that, you'll see that the ElementName binding doesn't work inside the control that has a code-behind (2nd in the list), but does work fine with the lookless control (bottom of the list).
What difference is there between lookless and user controls hosting a content presenter?
Edit: Added a solution to get the binding to work with this approach at the bottom.
Nice question.
AFAICT this is just because of the way the DP is setup and when the Binding is resolved respecting NameScope's.
From MainWindow.xaml you're assigning the DP MyContent property for the control with code-behind which takes the entire TextBox code as the DP value. Hence the binding is not resolved at this point.
When the Binding is applied, in the scope of the UserControl, the ElementName is not found, which we can verify if we add another TextBox into that UserControl, say
<StackPanel>
<TextBox Name="textBox" Text="Sampleeeeeeee" />
<ContentPresenter Content="{Binding MyContent, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type LookslessVsUserControl:MrWhiteWithCodeBehind}}}" />
</StackPanel>
Now we get
as the point the Binding is resolved the new TextBox is the one which is in scope not the one from MainWindow.xaml
As for the Style, the scope remains the same where the Style is applied, so it finds the MainWindow.xaml's text-box. We hence only have the one level of nesting, which we can also see from Snoop
Solution:
If this is the approach you prefer to set the DP with the control, you can still get the Binding to work:
In your MainWindow.xaml.cs you need to set the namescope for the UserControl accordingly so it doesn't use it's own WPF Xaml NameScope
In ctor() of MainWindow.xaml.cs:
Loaded += (sender, args) => NameScope.SetNameScope(problemControl, NameScope.GetNameScope(this));
and in xaml I named it problemControl such as:
<LookslessVsUserControl:MrWhiteWithCodeBehind x:Name="problemControl">
<LookslessVsUserControl:MrWhiteWithCodeBehind.MyContent>
<TextBlock Text="{Binding ElementName=textBox, Path=Text}" />
</LookslessVsUserControl:MrWhiteWithCodeBehind.MyContent>
</LookslessVsUserControl:MrWhiteWithCodeBehind>
With this when the UserControl loads and tries to resolve the Binding it should find the TextBox fine and give you the output you desire.

What is DataContext for?

As a continuation of the question Linking DataContext with another property in WPF.
At the very end of the research I was very surprised to find out that when one writes something like that:
<Label Content="{Binding Path=Name}" />
The DataContext against which the Content property is binded is of the Label control itself! The fact that it still works is due to the default inheritance of the DataContext value from the nearest parent.
But if you have this label wrapped in a custom control, and you don't want to bind your data to the DataContext property of that control, you would more likely love to have:
<Controls:SearchSettings Settings="{Binding Path=Settings}" />
And here you are. Now you need to set Settings as the DataContext for the SearchSettings control, for Label inside to bind against, but you can't, because that will trigger re-binding of Settings property.
I can't see the point in mixing binding properties using different sources: DataContext, by ElementName, etc.
So why would I ever use DataContext?
When you write
<Label name="myLabel" Content="{Binding Path=Name}" />
you are binding to myLabel.DataContext.Name, and not myLabel.Name.
The XAML in WPF is just a pretty user interface to display and interact with the actual data, otherwise known as the DataContext. The purpose of other binding sources (RelativeSource, ElementName, etc) is to point to another property that doesn't exist in the current control's DataContext
So suppose you have a Window. Without setting the DataContext, the window still displays but there is no data behind it.
Now suppose to set myWindow.DataContext = new ClassA();. Now the data that the window is displaying is ClassA. If ClassA has a property called Name, I could write a label and bind it to Name (such as your example), and whatever value is stored in ClassA.Name would get displayed.
Now, suppose ClassA has a property of ClassB and both classes have a property called Name. Here is a block of XAML which illustrates the purpose of the DataContext, and an example of how a control would refer to a property not in it's own DataContext
<Window x:Name="myWindow"> <!-- DataContext is set to ClassA -->
<StackPanel> <!-- DataContext is set to ClassA -->
<!-- DataContext is set to ClassA, so will display ClassA.Name -->
<Label Content="{Binding Name}" />
<!-- DataContext is still ClassA, however we are setting it to ClassA.ClassB -->
<StackPanel DataContext="{Binding ClassB}">
<!-- DataContext is set to ClassB, so will display ClassB.Name -->
<Label Content="{Binding Name}" />
<!-- DataContext is still ClassB, but we are binding to the Window's DataContext.Name which is ClassA.Name -->
<Label Content="{Binding ElementName=myWindow, Path=DataContext.Name}" />
</StackPanel>
</StackPanel>
</Window>
As you can see, the DataContext is based on whatever data is behind the UI object.
Update: I see this question so often from new WPF users that I expanded this answer into a post on my blog: What is this “DataContext” you speak of?
From CodeProject by kishore Gaddam:
DataContext is one of the most fundamental concepts in Data Binding. The Binding object needs to get its data from somewhere, and there are a few ways to specify the source of the data like using Source property directly in the Binding, inheriting a DataContext from the nearest element when traversing up in the tree, setting the ElementName and RelativeSource properties in the Binding object.
Detailed example on CodeProject: http://www.codeproject.com/Articles/321899/DataContext-in-WPF
In that particular case, you could do:
<Controls:SearchSettings DataContext="{Binding Path=Settings}" Settings="{Binding}" />
Assuming you want everything that may be content of the SearchSettings to use Settings as it's data context. Basically, the DataContext affects the element itself an any descendants that don't explicitly override it.
In most cases you do want to bind to the DataContext, in some templates on ItemsControls it is the only way to bind to the currently templated item for example. Further bindings to the DataContext are nice to write and read as they are concise.
In your example you can still set the DataContext, you only need to modify the binding on the Settings respectively:
<Controls:SearchSettings DataContext="{Binding Settings}" Settings="{Binding}"/>

How to set ItemsSource?

This dialog makes no sense to me
http://img576.imageshack.us/img576/4223/50709706.gif
And I'm having trouble finding good tutorials on it. Most of the examples aren't detailed enough, or do stuff via code, but I'd like to take advantage of the IDE as much as possible.
Whats the difference between ItemsSource and DataContext?
I'd like to bind it to just a List for starters. I don't need SQL or databases or anything fancy. Where would I declare my list? In MainWindow.xaml.cs? How do I get it to appear in that dialog?
Think of "DataContext" as the default value for "Source" in a binding.
When you create a binding, you can specify the path and source, like this (I'll use TextBox as an example):
<TextBox Text="{Binding Path=Foo,Source={StaticResource Bar}}" />
So my TextBox.Text property is bound to a Foo property on an object called Bar (a resource somewhere in the application).
However, if you have a whole bunch of things that you want to bind to properties on Bar, it's easier to set Bar as the DataContext of the parent container. A Binding without a Source will just use the DataContext by default, and DataContext flows through to child controls from the parent. So:
<StackPanel DataContext="{StaticResource Bar}">
<TextBox Text="{Binding Path=Foo}" />
<TextBox Text="{Binding Path=Fizz}" />
<TextBox Text="{Binding Path=Buzz}" />
</StackPanel>
All of the TextBoxes are still binding to properties on Bar, but they're doing it without setting it as a Source explicitly.
So let's have another look at the dialog you posted. It's giving you several options for the "source" of the ItemsSource binding. When you choose "DataContext", you're telling Visual Studio that the ItemsControl doesn't need to know the source - it'll pick it up from the DataContext of the parent container (maybe even the Window itself).
If you chose one of the other options (ElementName, RelativeSource or StaticResource) then you'd be setting the binding's source explicitly for that ItemsControl.
Once you've told it that it's binding to the DataContext, you'll need to drop into the "Path" section of the dialog and tell it which property to bind the items of the control to. In the end, the markup would look something like this (assuming it's a ListBox):
<ListBox ItemsSource="{Binding Path=Foos}" />
So the items in the ListBox are coming from a property called "Foos", and that property is on an object that's set in the DataContext somewhere higher in the logical tree (perhaps on the Window itself).
You rarely need to use the data context of a control outside of the control. The most common use case for setting DataContext(DataContext = this;) is within UserControl's code-behind to make all controls within the UserControl to bind to the control's properties.
When you use a ListBox, setting ItemsSource is sufficient, unless you are doing something funky.
This is a pretty good walkthrough: http://windowsclient.net/learn/video.aspx?v=315275
Specifically, you need to set the DataContext first to tell it where to look for the ItemsSource. The easiest way is to set this on the Window through the XAML:
<Window.DataContext>
<controllers:DownloadManager />
</Window.DataContext>

Resources