How to apply a Binding property to a WPF binding? - c#

I have a WPF UserControl. In the cs part, I have a property of type System.Windows.Data.Binding called ElementBinding.
Here is what the xaml file looks like:
<ItemsControl ItemsSource="{Binding ElementName=ControlRoot, Path=FilteredSource}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=ElementBinding, ElementName=ControlRoot}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
This doesn't work as intended as it displays (quite logically) the ToString() method of the Binding property.
What I would like to do is appy the Binding property of my UserControl to the Text property of my TextBlock, using the TextBlock DataContext.
I know I can always change my Binding property to a string property and use a converter and reflection to access the path, but I would prefer with a Binding property if it is possible. I guess it is since it's used, for example, in a DataGridColumn.

Related

Change Datacontext of a UserControl

I have to work with a UserControl, that I cannot change. The Datacontext of this UserControl is set to itself in its constructor.
public ParameterControl()
{
Datacontext = this;
}
The UserControl should be the template of my ListBox-Items.
<ListBox>
<ListBox.ItemTemplate>
<DataTemplate>
<parameterControl:ParameterControl
DataContext="{Binding ElementName=StepView, Path=Datacontext.SelectedStep}" //this doesn't work
</parameterControl:ParameterControl>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
My implemented binding for the datacontext doesn't work.
Does anyone know how I can solve this problem or tell me at what point of time the datacontexts are set?
Thanks for help,
Alex
EDIT:
Hi again,
there is no chance to rebuild the ParameterControl.
I've got this idea...
<ListBox
ItemsSource="{Binding Parameters}"
<ListBox.ItemTemplate>
<DataTemplate>
<parameterControl:ParameterControl
ParamName="{Binding <!--To the item in the ItemsSource-Collection-->}"
</parameterControl:ParameterControl>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The parameter Control needs only the name for the ParamName property to be displayed correctly.
And this name is in the item of the ItemsSource-Collection.
Do anyone now how to bind?
Thanks for help, Alex
A UserControl that is used in the ItemTemplate of an ItemsControl must not explicitly set its DataContext property, because doing so prevents inheriting the DataContext from the item container (e.g. the ListBoxItem here).
The only valid solution for this problem is to remove the DataContext assignment from the UserControl's constructor, and to replace any possible "internal" DataContext-based bindings by RelativeSource or ElementName bindings, like
<TextBlock
Text="{Binding SomeText, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
where SomeText is a property of the UserControl class.
As a general rule, never set the DataContext property of a UserControl explicitly.

Binding Property of an Item in an ItemsControl From a Collection

My theory code:
ScriptContainerUserControl.xaml
<ItemsControl x:Name="ScriptItemsControl">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<TextBox x:Name="pTB" Text="{Binding PhasePriority}" />
<TextBox x:Name="nTB" Text="{Binding Name}" />
<TextBox x:Name="dTB" Text="{Binding Description}" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
ScriptContainerUserControl.xaml.cs
public ScriptContainerUserControl() : base()
{
InitializeComponent();
ScriptItemsControl.ItemsSource = PScriptCollection;
}
//PScriptCollecion is of type SynchronizedObservableCollection<ProcessScript>
//ProcessScript has the elements PhasePriority, Name, and Description
Would the code above work for making sure
ScriptItemsControl[i].dTB.Text = PScriptCollection[i].Description?
Or is it not possible to bind like this?
Fenster,
It should definitely work, provided you have getter setter properties implemented for all the three properties in ProcessScript class.
When you use a datatemplate - it means you are setting the datacontext of each element of your itemscontrol to an element of your collection.
so here each Itemcontrol element will look at ProcessScript object and if that object has all three properties , you should see the data.
It is not possible to do it in this way. You do not set Binding actually... To have support for observing a changes on collection you should bind the collection to ItemsSource property of ItemsControl.
Instead of line:
ScriptItemsControl.ItemsSource = PScriptCollection;
try this
ScriptItemsControl.ItemsSource = new Binding("PScriptCollection");

WPF:Difference between TabControl.ItemTemplate and TabItem.ContentTemplate

I'm confused on this for a long time,these both seem to affect the tabitems' presentation in the tabcontrol.
Is it designed for best control of the presentation of the tabcontrol?
Or if there is something I dont't understand.
There's some very long answers here for what is actually a very simple question. To avoid confusion:
ItemTemplate is the template used to format each item in the ItemsSource to create the headers (the controls that appear in the tab bar) and ContentTemplate is the template used to format each item in the ItemsSource to create the content of the tabs (the controls that appear when you click on the header).
The ItemsControl.ItemTemplate Property is used to define what each item in the data bound collection should look like... from the ItemsControl.ItemTemplate Property page on MSDN:
Gets or sets the DataTemplate used to display each item.
As you can see, it is of type DataTemplate, which is customary for a template that displays data... its DataContext will automatically be set to an item from the collection and so controls declared in that DataTemplate will automatically have access to the items properties. Please see the Data Templating Overview page on MSDN for further help with this.
Similarly, from MSDN, the ContentControl.ContentTemplate Property:
Gets or sets the data template used to display the content of the ContentControl.
Again, its DataContext will automatically be set to the object that is set as the Content property. Please note that the ContentControl only has a ContentTemplate Property and no ItemTemplate Property, which is used for collection items... from the Data Templating Overview page on MSDN:
Because myTaskTemplate is a resource, you can now use it on other controls that have a property that takes a DataTemplate type. As shown above, for ItemsControl objects, such as the ListBox, it is the ItemTemplate property. For ContentControl objects, it is the ContentTemplate property.
UPDATE >>>
To clarify this situation further, think of this simple rule:
Use the ContentTemplate property to define how an object that is set as the Content property of a ContentControl should look.
Use the ItemTemplate property to define how the items of a collection control should look.
That the difference at its simplest. However, I'd like to point out that as these properties are both of type DataTemplate, their values are interchangeable.
For example, let's say that you have a Person class and you display a collection of Person objects in a ListBox. You can declare a DataTemplate to set as the ListBox.ItemTemplate property to define how each Person in the collection should look. However, if you just wanted to display a single Person, then you could use a ContentControl with the Content set to an instance of the Person class, and still use the same DataTemplate, but set as the ContentTemplate instead:
Multiple objects:
<ListBox ItemsSource="{Binding People}" ItemTemplate="{StaticResource Template}" ... />
...
Single object:
<ContentControl Content="{Binding Person}"
ContentTemplate="{StaticResource Template}" ... />
Setting the TabControl.ItemTemplate you specify a template to use for all TabItems in the Items collection of the TabControl, unless you override the TabItem.ContentTemplate for a specific TabItem.
So, while they do the same, TabControl.ItemTemplate is a more generic template for all the TabItems in the TabControl and TabItem.ContentTemplate is specific for the TabItem it is used in.
The above is not quite true, as TabControl has an ItemTemplate property and a ContentTemplate property, to make it more confusing.
ItemTemplate is used as template for the header (the tab thingy) of all TabItems added through databinding on the ItemsSource or through Xaml without making the the added item a TabItem:
<TabControl ItemsSource="{Binding ListOfItems}">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" Foreground="Red"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" Foreground="Blue"/>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
This will create a TabControl with red text in the header/tab and blue text for content.
Now, if we do the following:
<TabControl>
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" Foreground="Red"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" Foreground="Blue"/>
</DataTemplate>
</TabControl.ContentTemplate>
<TabItem Header="One" Content="One"/>
<TabItem Header="Two" Content="Two"/>
<TabItem Header="Three" Content="Three"/>
</TabControl>
We'll have a TabControl with three tabs, and the header text is black, content is still blue. And a DataError informing us that the ItemTemplate and ItemTemplateSelector properties are ignored for items already of the ItemsControl's container type, in this case TabItem. In this case, we need to specify TabItem.HeaderTemplate to change the appearance of the header.
So TabControl.ItemTemplate and TabItem.ContentTemplate don't do the same, but my previous explanation still holds for TabControl.ContentTemplate and TabItem.ContentTemplate.

DataBinding in TemplateSelector

I'm using a few data templates to display different values, those data templates are chosen by DataTemplateSelector. Every control has some DataBinding to my custom objects. Objects are part of an ObservableCollection and then DTS is choosing the template for them. The problem is: When I try to run my app with some pre defined objects (in code) the selected controls has no values. Ex:
<!--Date Template-->
<DataTemplate x:Key="DateTemplate">
<WrapPanel x:Name="DateTemplate_Panel">
<WrapPanel.DataContext>
<params:FTParams />
</WrapPanel.DataContext>
<Label x:Name="DateTemplate_Label" Content="{Binding Path=Name}" />
<DatePicker x:Name="DateTemplate_DatePicker" SelectedDate="{Binding Path=SelectedValue}" SelectedDateFormat="Long" />
</WrapPanel>
</DataTemplate>
Controls are responding only when I change their value (INotifyPropertyChanged is implemented)
If I set
<Label Content="{Binding Path=SelectedValue}"/>
and I select a date in DataPicker then the content is loaded correctly. But I really need to have this values loaded on startup.
Can you give me some advice?
The data template should not have embedded data. And you definitely don't want to instantiate instances of FTParams, from within the DT. The DataContext property of the DataTemplate is set implicitly, when you have the data somewhere else in the tree.
I assume you have some sort of ItemsControl, but for the simplicity, let the sample below have a content control:
<ContentControl ContentTemplate="{StaticResource DateTemplate}">
<params:FTParams />
</contentControl>
If you had all your items in ItemsControl (with ItemsSource bind to the ObservableCollection), then instead of ContentTemplate you should set the ItemsTemplate, or if you want to work with template selector, set the ItemTemplateSelector.
<ItemsControl ItemsSource="{Binding PathToTheObsCollectionProperty}"
ItemTemplateSelector="{StaticResource MySelector}" />
In all cases, the DT should not have explicitly set the DataContext property.
Then have your data template without the DataContext element.

WPF binding an image column in a datagrid through a converter, can't update image on property change

I have this datagrid that is bound to an observablecollection of items, like this:
<DataGrid ItemsSource="{Binding Path=MyItems}">
Then, one of the columns is bound to a property of MyItems through a simple converter that switches the bool to an image path.
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image Name="DownloadedIcon" Source="{Binding Converter={StaticResource BoolToImageCheckmark}, ConverterParameter=IsDownloaded, UpdateSourceTrigger=PropertyChanged}" Width="16" Height="16" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
The property itself, IsDownloaded, fully implements INotifyPropertyChanged.
This works normally, as the data displayed matches the values of the collection, and the image column properly displays the image based on the property value.
Trouble comes when the property changes. If I bind a text column directly on the property, the content will update when the property is updated. However, the image column, which passes through a converter, will not receive the notification to update.
Any ideas?
Try this:
<DataTemplate>
<Image Name="DownloadedIcon" Source="{Binding Path=IsDownloaded,Converter={StaticResource BoolToImageCheckmark}}" Width="16" Height="16" />
</DataTemplate>
Also place a breakpoint in the converter, in order to verify the binding is actually working.
Note you'll get the bound value via the Value parameter in your converter.
values passed to ConverterParameter do not react to PropertyChanged notifications. Use Path instead of ConverterParameter in your binding, then refer to the value argument in the Convert() function in your converter instead of the parameter argument.
ConverterParameter is not a dependency property and therefore you cannot bind it to a property like you tried to do it. You should bind your image source to the IsDownloaded property and convert that:
<DataTemplate>
<Image Name="DownloadedIcon" Source="{Binding Path=IsDownloaded,Converter={StaticResource BoolToImageCheckmark}}" Width="16" Height="16" />
</DataTemplate>
Problem is in your converter class.
Since your binding expression do not specify a “Path”, current DataContext use as the path and results in the DataContext object as your value in your converter class. Calculations are performing on this copy of datacontext object.
This approach will succeed at first time when binding being executed. As a result the image column properly displays the image.
Later ‘IsDownloaded’ property changes, it reflects in ObservableCollectionClass, but the image control unable to understand this change since its source property is not bound to any collection class property. Similarly as the converter class received a copy of datacontext object, property changes never reflects in converter class either.
Therefore set the image source property to collection class property 'IsDownloaded'. Any changes happens to this property will trigger the converter class with new value.
Image Name="DownloadedIcon" Source="{Binding Path=IsDownloaded,Converter={StaticResource BoolToImageCheckmark}}" Width="16" Height="16"/>
UpdateSourceTrigger not required.
in fact, you didn't bind the image to the property IsDownloaded you bind it to the whole object in the list. Path is Important.
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image Name="DownloadedIcon" Source="{Binding Converter={StaticResource BoolToImageCheckmark}, ConverterParameter=IsDownloaded, Path=IsDownloaded, UpdateSourceTrigger=PropertyChanged}" Width="16" Height="16" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

Categories