How do I bind to RelativeSource Self? - wpf

I am trying to bind several different properties in my Xaml:
<Label Content="{Binding Description}"
Visibility="{Binding Path=DescriptionVisibility,
ElementName=_UserInputOutput}"
FontSize="{Binding Path=FontSizeValue, ElementName=_UserInputOutput}"
HorizontalAlignment="Left" VerticalAlignment="Top" Padding="0" />
You will noticed I have used two Different binding techniques here. The ones using Element Name work, the other does not. Here is code behind:
public string Description
{
get { return (string)GetValue(DescriptionProperty); }
set { SetValue(DescriptionProperty, value); }
}
public static readonly DependencyProperty DescriptionProperty =
DependencyProperty.Register("Description", typeof(string), typeof(UserControl),
new UIPropertyMetadata(""));
Each Binding has a different name but they all look like this for the most part.
I want my Binding to be able to work with:
{Binding Description}
Instead of:
{Binding Path=Description, ElementName=_UserInputOutput}
It only seems to be working when ElementName is used. I need to export/import this XAML, so I can't have a ElementName or the import won't work.
I thought this would be best:
{Binding Path=Description, RelativeSource={RelativeSource Self}}
This did not work.
Any ideas?? Thank you!

{RelativeSource Self} targets the object that owns the property that is being bound, if you have such a binding on a Label it will look for Label.Description, which isn't there. Instead you should use {RelativeSource AncestorType=UserControl}.
Bindings without a source (ElementName, Source, RelativeSource) are relative to the DataContext, however in UserControls you should avoid setting the DataContext to not mess with external bindings.

You haven't set the DataContext, which is what the RelativeSource is using to determine what it's relative to. You need to set the DataContext at a higher level, like the UserControl. I typically have:
<UserControl ... DataContext="{Binding RelativeSource={RelativeSource Self}}">
</UserControl>
This tells the UserControl to bind itself the class in the codebehind.

Related

How ist the DataContext of a Unsercontrol related to its DependencyProperties?

while working with UserControls having DependencyProperties i realized that it is curcial to consider where to set the DataContext. To picture it ive created a sample application. There are two UserControls, both equal except on where the DataContext is set:
Working UserControl:
<UserControl x:Class="DpropTest.OkUserControl"
...>
<Grid DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=dpropTest:OkUserControl }}">
<TextBlock Text="{Binding Path=MyDepProp}"></TextBlock>
</Grid>
</UserControl>
Not working user control:
<UserControl x:Class="DpropTest.NotOkUserControl"
...
DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=dpropTest:NotOkUserControl}}"
>
<Grid >
<TextBlock Text="{Binding Path=MyDepProp}"></TextBlock>
</Grid>
Both UserControls have a DependencyProperty called MyDepProp,
#region Dependency Property Declaration
public static readonly DependencyProperty MyDepPropProperty = DependencyProperty.Register(
"MyDepProp", typeof(string), typeof(NotOkUserControl), new PropertyMetadata(default(string)));
public string MyDepProp
{
get { return (string)GetValue(MyDepPropProperty); }
set { SetValue(MyDepPropProperty, value); }
}
#endregion Dependency Property Declaration
This is how i integrated the UserControls to the mainWindow:
<Grid x:Name="ParentGrid">
<StackPanel>
<dpropTest:OkUserControl MyDepProp="{Binding Path=ActualWidth, ElementName=ParentGrid}"/>
<dpropTest:NotOkUserControl MyDepProp="{Binding Path=ActualWidth, ElementName=ParentGrid}"/>
</StackPanel>
</Grid>
The running application shows the actualWith for the first UserControlonly only, the second UserControl remains unset as the DP doesnt bind.
There is no error in the output window regarding the second UserControl...
Maybe there is an WPF Pro out there with an brief explanation?
Thank you!
Uli
I don't think FindAncestor will start with the element itself, but apart from that: you can either set this on the UserControl:
DataContext="{Binding RelativeSource={RelativeSource Self}}"
or set this in the constructor of the user control, before the InitializeComponent:
DataContext = this;
As a sidenote: it's often not necessary to bind with the ActualWidth of some ancestor; in this case the width of the stackpanel is the same as the width of its parent grid and the width of the usercontrols is the same as the width of ths stackpanel. So in effect MyDepProp is equal to the ActualWidth of the usercontrol.
<UserControl x:Class="DpropTest.NotOkUserControl"
...
DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=dpropTest:OkUserControl }}"
Seems to me you are binding to wrong parent! You are inside NotOkUserControl but you are asking for unreachable AncestorType...

Referencing a UIElement in a ViewModel from XAML

I'm relatively new to using WPF and the MVVM architecture. I have a question about referencing UIelements from a XAML window's DataContext.
I have menu items that are bound to Views DataContext using this syntax:
<MenuItem Header="About" Command="{Binding AboutCommand}" />
I'd like to use a similar paradigm to add items to a grid. Right now I am using a class WorkflowDesigner. I can add it to my grid using the following code in my ViewModel:
grid.AddChildren(wd.View)
where view is of type UIElement.
What I'd rather do is add is reference to it from my XAML file without putting anything in my codebehind so that I can use the XAML mostly as a skin. Is it possible to use a tag just takes its UIElement from the datacontext of the XAML file?
This is possible, but it's not within the spirit of MVVM to have your ViewModel provide controls to your view. Ideally your ViewModel should have no dependencies on System.Windows.Controls at all.
If you must, then you can use a ContentControl:
<ContentControl Content={Binding wd.View} />
To handle this I'd create a ViewLocator class and put an instance of it into your resource dictionary. Then use this:
<ContentControl Content={Binding Source={StaticResource ViewLocator}, Path=WorkflowDesigner} />
I'm not sure if I quite understand your problem, but if you have a class you wish to present in your view from your ViewModel, you could use an ItemsControl to display different classes using a DataTemplate.
Say you have class User
public class User
{
public string Id { get; set;}
public string Name { get; set;}
}
public class UserViewModel
{
private ObservableCollectionaUser<User> _users = new......
public ObservableCollection<User> Users
{
get
{
return _users;
}
}
}
In UserView, you could have
<ItemsControl ItemsSource="{Binding Users}">
<ItemsControl.Resources>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Id}" />
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
This way, a User would be presented in the view using the template declared above. Then you would not have to use UIElements in your ViewModel.
The ItemsControl could refer to grid items, and have items presented in a grid with SharedGridScope (if I remember correctly).

Binding with Static properties in ListView ItemTemplate

I'm having some problems with WPF binding.
I have an assembly with some const properties in class Values, that correspond to columns from datatable.
I want to bind the value from a column to a TextBlock using the const property to specify the column at a ListView ItemTemplate like shown in the code:
xmlns:C="clr-namespace:WPFApplication1.Entities;assembly=WPFApplication1">
<Grid>
<ListView>
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding {x:Static C:Values.FieldCode}}" /> /*<- Don't work*/
/*Works like this: <TextBlock Text="{Binding [CODE]}" />*/
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
If I use binding with the static property I I'm not able to show the value in the datarow but if I use the Binding like this [CODE] I'm able to show the value.
What is appening?
Any clue?
Thanks in advance.
You need to use your static property as the Source, not the Path, which is the default attribute for Binding:
{Binding Source={x:Static C:Values.FieldCode}}
the italic text is not correct, please read from EDIT1:
It is not possible to bind to static properties. Binding always needs an instance of the Class. It is possible by instantiating the class as resource of in the code behind and set that class as the datacontext
EDIT1:
Add a static property of type
public static string FieldCode = "Code";
public static PropertyPath FieldCodePath = new PropertyPath(FieldCode);
Change the Binding to the binding below:
<TextBlock Text="{Binding Path={x:Static C:Values.FieldCodePath}, IsAsync=true}" />
I hope this helps

Accessing codebehind object in XAML

Another post describes how to access a codebehind variable in XAML.
However, I'd like to access a variable in codebehind object from XAML. The codebehind object, called FeedData, is declared as a dependency property of type FeedEntry. This class is just a container class with string and datetime properties.
Codebehind's property definitition is this:
public FeedEntry FeedData
{
get { return (FeedEntry)GetValue(FeedDataProperty); }
set { SetValue(FeedDataProperty, value); }
}
public static readonly DependencyProperty FeedDataProperty =
DependencyProperty.Register("FeedData", typeof(FeedReaderDll.FeedEntry), typeof(FeedItemUserControl),
new FrameworkPropertyMetadata(new FeedEntry(){ Title="Hi!", Published=DateTime.Now }));
In XAML I'm doing this, which doesn't work:
<UserControl x:Class="FeedPhysics.UserControls.FeedItemUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="40" Width="200"
Background="Blue"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
x:Name="xRoot">
<StackPanel>
<TextBlock Text="{Binding Title}" Foreground="White"/>
<TextBlock Text="{Binding Published}" Foreground="White"/>
</StackPanel>
</UserControl>
But if I override Window's datacontext setting in codebehind's contructor, it will work! Like this:
xRoot.DataContext = FeedData;
I understand why it works when datacontext is set in codebehing. But I'd like to find out a way to grab variables within an object that is declared in codebehind. Because, everything should be doable from XAML, right?
Thanks for answers in advance.
Try setting the StackPanel's DataContext to the FeedData object:
<StackPanel DataContext="{Binding FeedData}">
...
This will force the StackPanel to look at the DependencyProperty, and all elements in it will be referenced as properties of FeedData.
As long as you define the DataContext as "FeedData" somewhere in the logical tree above the visual elements you are binding to properties of it, it will work.

WPF binding to Listbox selectedItem

Can anyone help with the following - been playing about with this but can't for the life of me get it to work.
I've got a view model which contains the following properties;
public ObservableCollection<Rule> Rules { get; set; }
public Rule SelectedRule { get; set; }
In my XAML I've got;
<ListBox x:Name="lbRules" ItemsSource="{Binding Path=Rules}"
SelectedItem="{Binding Path=SelectedRule, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Name:" />
<TextBox x:Name="ruleName">
<TextBox.Text>
<Binding Path="Name" UpdateSourceTrigger="PropertyChanged" />
</TextBox.Text>
</TextBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
Now the ItemsSource works fine and I get a list of Rule objects with their names displayed in lbRules.
Trouble I am having is binding the SelectedRule property to lbRules' SelectedItem. I tried binding a textblock's text property to SelectedRule but it is always null.
<TextBlock Text="{Binding Path=SelectedRule.Name}" />
The error I'm seeing in the output window is:
BindingExpression path error: 'SelectedRule' property not found.
Can anyone help me with this binding - I can't see why it shouldn't find the SelectedRule property.
I then tried changing the textblock's text property as bellow, which works. Trouble is I want to use the SelectedRule in my ViewModel.
<TextBlock Text="{Binding ElementName=lbRules, Path=SelectedItem.Name}" />
Thanks very much for your help.
First off, you need to implement INotifyPropertyChanged interface in your view model and raise the PropertyChanged event in the setter of the Rule property. Otherwise no control that binds to the SelectedRule property will "know" when it has been changed.
Then, your XAML
<TextBlock Text="{Binding Path=SelectedRule.Name}" />
is perfectly valid if this TextBlock is outside the ListBox's ItemTemplate and has the same DataContext as the ListBox.
Inside the DataTemplate you're working in the context of a Rule, that's why you cannot bind to SelectedRule.Name -- there is no such property on a Rule.
To bind to the original data context (which is your ViewModel) you can write:
<TextBlock Text="{Binding ElementName=lbRules, Path=DataContext.SelectedRule.Name}" />
UPDATE: regarding the SelectedItem property binding, it looks perfectly valid, I tried the same on my machine and it works fine. Here is my full test app:
XAML:
<Window x:Class="TestWpfApplication.ListBoxSelectedItem"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ListBoxSelectedItem" Height="300" Width="300"
xmlns:app="clr-namespace:TestWpfApplication">
<Window.DataContext>
<app:ListBoxSelectedItemViewModel/>
</Window.DataContext>
<ListBox ItemsSource="{Binding Path=Rules}" SelectedItem="{Binding Path=SelectedRule, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Name:" />
<TextBox Text="{Binding Name}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Window>
Code behind:
namespace TestWpfApplication
{
/// <summary>
/// Interaction logic for ListBoxSelectedItem.xaml
/// </summary>
public partial class ListBoxSelectedItem : Window
{
public ListBoxSelectedItem()
{
InitializeComponent();
}
}
public class Rule
{
public string Name { get; set; }
}
public class ListBoxSelectedItemViewModel
{
public ListBoxSelectedItemViewModel()
{
Rules = new ObservableCollection<Rule>()
{
new Rule() { Name = "Rule 1"},
new Rule() { Name = "Rule 2"},
new Rule() { Name = "Rule 3"},
};
}
public ObservableCollection<Rule> Rules { get; private set; }
private Rule selectedRule;
public Rule SelectedRule
{
get { return selectedRule; }
set
{
selectedRule = value;
}
}
}
}
Yocoder is right,
Inside the DataTemplate, your DataContext is set to the Rule its currently handling..
To access the parents DataContext, you can also consider using a RelativeSource in your binding:
<TextBlock Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ____Your Parent control here___ }}, Path=DataContext.SelectedRule.Name}" />
More info on RelativeSource can be found here:
http://msdn.microsoft.com/en-us/library/system.windows.data.relativesource.aspx
For me, I usually use DataContext together in order to bind two-depth property such as this question.
<TextBlock DataContext="{Binding SelectedRule}" Text="{Binding Name}" />
Or, I prefer to use ElementName because it achieves bindings only with view controls.
<TextBlock DataContext="{Binding ElementName=lbRules, Path=SelectedItem}" Text="{Binding Name}" />
There is a shorter version to bind to a selected item's property:
<TextBlock Text="{Binding Rules/Name}" />
since you set your itemsource to your collection, your textbox is tied to each individual item in that collection. the selected item property is useful in this scenario if you were trying to do a master-detail form, having 2 listboxes. you would bind the second listbox's itemsource to the child collection of rules. in otherwords the selected item alerts outside controls that your source has changed, internal controls(those inside your datatemplate already are aware of the change.
and to answer your question yes in most circumstances setting the itemsource is the same as setting the datacontext of the control.

Resources