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.
Related
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...
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.
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
I made a UserControl that is meant to be updated once every few seconds with data from a serial port. This UserControl should be very simple, consisting of a Label for a field name, and another Label containing the field value. I say that it should be simple, but it doesn't work. It does not update at all, and doesn't even display the field name.
Below is the code:
public partial class LabeledField : UserControl {
public LabeledField() {
InitializeComponent();
}
public string fieldName {
get { return fieldNameLabel.Content.ToString(); }
set { fieldNameLabel.Content = value; }
}
public string fieldValue {
get { return (string)GetValue(fieldValueProperty); }
set { SetValue(fieldValueProperty, value); }
}
public static readonly DependencyProperty fieldValueProperty =
DependencyProperty.Register(
"fieldValue",
typeof(string),
typeof(LabeledField),
new FrameworkPropertyMetadata(
"No Data"
)
)
;
}
Here is the XAML:
<UserControl x:Class="DAS1.LabeledField" Name="LF"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel Orientation="Horizontal">
<Label Width="100" Height="30" Background="Gray" Name="fieldNameLabel" />
<Label Width="100" Height="30" Background="Silver" Name="fieldValueLabel" Content="{Binding fieldValue}" />
</StackPanel>
And here is the XAML for the Window which references the UserControl. First the header:
<Window x:Class="DAS1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:me="clr-namespace:DAS1"
Title="Window1" Height="580" Width="780">
Then the UserControl itself:
<me:LabeledField fieldName="Test" Width="200" Height="30" fieldValue="{Binding businessObjectField}"/>
If I knew of a more specific question to ask, I would--but can anyone tell me why this doesn't work?
Turns out that in the XAML for the user control, the binding was incorrectly specified.
Originally it was:
<Label Width="100" Height="30" Name="fieldValueLabel" Content="{Binding fieldValue}" />
But I had not specified the element to which fieldValue belongs. It should be (assuming my user control is named "LF":
<Label Width="100" Height="30" Name="fieldValueLabel" Content="{Binding ElementName=LF, Path=fieldValue}" />
If you want to bind to properties of the control, you should specify so in the binding. Bindings are evaluated relative to DataContext if their source isn't explicitly specified, so your binding doesn't bind to your control, but to the inherited context (which is likely missing the property you're binding to). What you need is:
<Label Width="100" Height="30" Name="fieldValueLabel"
Content="{Binding Path=fieldValue, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type DAS1.LabeledField}}}" />
You really don't need a dependency property on your user control, in fact you should strive to keep user controls without anything special in the code-behind, custom Controls should be used for that.
You should define your UserControl like this (without any code behind):
<UserControl x:Class="DAS1.LabeledField"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel Orientation="Horizontal">
<Label Width="100" Height="30" Name="fieldNameLabel" Content="{Binding fieldName}" />
<Label Width="100" Height="30" Name="fieldValueLabel" Content="{Binding field}" />
</StackPanel>
Then, make sure your business object implements INotifyPropertyChanged, because you cannot update from your business object efficiently without modifying it at least this much. The fieldName is just an example how you could bind the display name on the label automatically to a property on your business object.
Then, simply make sure the DataContext of your UserControl is your business object.
How will this work? The Label.Content property is a DependencyProperty and will support binding itself. Your business object implements INotifyPropertyChanged and thus supports updates to binding - without it, the binding system does not get notified when your field's value changes, regardless if you bound it to a DependencyProperty on one end.
And if you want to reuse this user control elsewhere, simply place a desired instance of your business object to the DataContext of the desired LabeledField control. The binding will hook up itself from the DataContext.
I've create user control like this:
public partial class View
{
public View()
{
InitializeComponent();
}
public static DependencyProperty NameProperty = DependencyProperty.Register("Name", typeof(string), typeof(TeaserView) );
public string Name
{
get { return (string)GetValue(NameProperty); }
set { SetValue(NameProperty, value); }
}
}
XAML:
<UserControl x:Class="Controls.View"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="200" Width="164">
<Grid VerticalAlignment="Stretch"
x:Name="Preview">
<Label Height="28" Content="{Binding ElementName=Preview, Path=Name}" Background="LightYellow" x:Name="name" VerticalAlignment="Top" ></Label>
</Grid>
</UserControl>
and use it in Window1 simply in XAML:
<controls:View Height="200" Name="View1" Width="164" />
and I try set the Content in C# (Name property in this sample) but it does'n work, label's content is still empty. (All refereces, etc. are good) What's wrong?
Your code is wrong. You bind to Grid.Name property, which is "Preview", not to View.Name.
I really encourage you to go read from A to Z "DataBinding Overview" on MSDN. It worth your time, trust me :). In fact whole "Windows Presentation Foundation" section would be worth your attention.
As for your code, the following will work:
<UserControl x:Class="WpfApplication5.View"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300"
Width="300"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<Label Height="28"
Content="{Binding Path=Name}"
Background="LightYellow"
VerticalAlignment="Top"/>
</Grid>
</UserControl>
But are you sure you want to hide "Name" property from parents?
Have you set the datacontext on the user control? Try setting it to point to its own codebehind:
DataContext="{Binding RelativeSource={RelativeSource Self}}"
I've put the Name property just as sample. I'm trying to set Label Content in Window1.xaml.cs like:
View1.Name = "Casablanca";
Try the following binding, it should work:
<Label Content="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:View}}, Path=Name}" />
You should also define a xmlns:local="whatever_path_you_have" on the top of the file.
I also suggest renaming "Name" DP to something else to avoid name collusion.
Copied your exact code and it works fine.
However, it's not doing what you're probably expecting it to do. You're setting the source of the binding to the Grid instance. Therefore, the Name property will yield "Preview". The Name property you've defined in your UserControl is ignored because there's already a Name property on UserControl.