I have a ListBox:
<ListBox x:Name="HistogramListBox" Grid.Column="1" Margin="8,2,8,0"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Template="{StaticResource HistogramListBoxControlTemplate}"
ItemContainerStyle="{StaticResource HistogramListBoxItem}"
ItemTemplate="{DynamicResource BucketTemplate}" />
That uses a DataTemplate that in turn uses a ValueConverter to determine the height of the ListBoxItem:
<DataTemplate x:Key="BucketTemplate">
<StackPanel>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="100"/>
</Grid.RowDefinitions>
<StackPanel HorizontalAlignment="Stretch" VerticalAlignment="Bottom">
<Rectangle Grid.Row="0" StrokeThickness="1" VerticalAlignment="Bottom"
Stroke="{Binding ElementName=MainElement, Path=BucketStroke}"
Fill="{Binding ElementName=MainElement, Path=BucketFill}" >
<Rectangle.Height>
<MultiBinding Converter="{StaticResource HistogramValueToPercentageConverter}">
<Binding Mode="OneWay" Path="ItemCount" />
<Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type local:Histogram}}" />
</MultiBinding>
</Rectangle.Height>
</Rectangle>
</StackPanel>
</Grid>
</StackPanel>
</DataTemplate>
The ListBox ItemsSource is an int[].
When I execute the code it says it can't find 'ItemCount' on an Int32. I thought it got the item count from the ListBox (I'm obviously wrong).
Can someone tell me how I can get my ValueConverter to know what item I am on.
Thanks
Dan
Assuming your first converter parameter is intended to be the actual value being charted and the second the Histogram object:
<Rectangle.Height>
<MultiBinding Converter="{StaticResource HistogramValueToPercentageConverter}">
<Binding />
<Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type local:Histogram}}" />
</MultiBinding>
</Rectangle.Height>
This is because the DataContext is the integer itself, at least that appears to be the case from the error message you gave.
By the way, you would normally set the ListBox's ItemsSource using a binding, not from code-behind. This leads to much cleaner separation of UI and code. I noticed no ItemsSource= was shown in your example code so I thought I should mention this.
The data context of the items in the data template is the data item itself, which is an int. If you want to a property on the ListBox, you'll need to reach outside of your current context to do so. You can use a RelativeSource to do this:
{Binding Items.Count, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}}
You can try this for your Binding:
<Binding Path="Items.Count">
<Binding.RelativeSource>
<RelativeSource AncestorType="{x:Type ListBox}" />
</Binding.RelativeSource>
</Binding>
Related
Is it possible to access a property value from the StringFormat binding property in XAML? I mean, this is my XAML:
<TextBox Name="costBlock" Grid.Row="4" Grid.Column="1" Margin="4" IsEnabled="False"
Text="{Binding DataContext.CalculatedCost, Mode=OneWay, StringFormat={}{0} €}"></TextBox>
I need to replace the "€" symbol in the StringFormat bindingby the symbol of the selected currency, which is a property in a static class: Settings.SelectedCurrencySymbol.
How can I do it?
Thanks to Clemens reccommendation I found this solution:
<TextBlock Name="costBlock" Grid.Row="4" Grid.Column="1" Margin="4"
IsEnabled="False">
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} {1}">
<Binding Path="DataContext.CalculatedCost" />
<Binding Path="(shared:Settings.SelectedCurrencySymbol)" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
Below tabitem3 is working fine.
I would like to get away from naming the controls and pass the properties by RelativeSource.
The code that is failing is
Binding RelativeSource="{RelativeSource AncestorType={x:Type Expander}}" Path="IsExpanded"
The error in the converter is dependency object not set
The Expander is a sibling not an ancestor.
How can I find that sibling (without an x:Name)?
<TabItem x:Name="tabitem3" IsSelected="False">
<TabItem.Header>
<Expander x:Name="tabexp3" Header="Three" IsHitTestVisible="True"
Expanded="expcolp" Collapsed="expcolp" IsExpanded="False"/>
</TabItem.Header>
<TextBlock Text="Content Three TabItem" Background="LightBlue" >
<TextBlock.Visibility>
<MultiBinding Converter="{StaticResource bvc2}" Mode="OneWay">
<Binding ElementName="tabexp3" Path="IsExpanded"/>
<Binding ElementName="tabitem3" Path="IsSelected" />
</MultiBinding>
</TextBlock.Visibility>
</TextBlock>
</TabItem>
<TabItem IsSelected="False">
<TabItem.Header>
<Expander Header="Four" IsHitTestVisible="True"
Expanded="expcolp" Collapsed="expcolp" IsExpanded="False"/>
</TabItem.Header>
<TextBlock Text="Content Four TabItem" Background="LightBlue" >
<TextBlock.Visibility>
<MultiBinding Converter="{StaticResource bvc2}" Mode="OneWay">
<Binding RelativeSource="{RelativeSource AncestorType={x:Type Expander}}" Path="IsExpanded"/>
<Binding RelativeSource="{RelativeSource AncestorType={x:Type TabItem}}" Path="IsSelected"/>
</MultiBinding>
</TextBlock.Visibility>
</TextBlock>
</TabItem>
I am not sure that Expander is a real sibling of the TextBlock. The first one is a child of TabItem's header, the former is a child of TabItem's content.
Anyway if you do not want to use naming (indeed I do not like it too), you can "go up" through the logical tree by looking for a TabItem ancestor and then you can "go down" by using the right path.
The result is this binding:
<Binding RelativeSource="{RelativeSource AncestorType={x:Type TabItem}}" Path="Header.IsExpanded" />
I hope it can help you.
EDIT
To test my binding you can use this simple XAML:
<TabControl>
<TabItem IsSelected="True">
<TabItem.Header>
<Expander Header="One" IsHitTestVisible="True" IsExpanded="False"/>
</TabItem.Header>
<TabItem.Content>
<TextBlock Text="Some contents..." />
</TabItem.Content>
</TabItem>
<TabItem IsSelected="False">
<TabItem.Header>
<Expander Header="Two" IsHitTestVisible="True" IsExpanded="False"/>
</TabItem.Header>
<TabItem.Content>
<TextBlock>
<TextBlock.Text>
<Binding RelativeSource="{RelativeSource AncestorType={x:Type TabItem}}" Path="Header.IsExpanded" />
</TextBlock.Text>
</TextBlock>
</TabItem.Content>
</TabItem>
</TabControl>
If you expand/collapse the second Expander, the TextBlock's text will change.
My binding does not work if the TabItem has IsSelected set to true. In this case you can extend my idea in this way:
<Binding RelativeSource="{RelativeSource AncestorType={x:Type TabControl}}" Path="SelectedItem.Header.IsExpanded" />
I have a ComboBox defined as below:
<ComboBox Width="200" Height="30" Grid.Column="0" x:Name="ExistingSpeciesComboBox"
ItemsSource="{Binding SpeciesColorCollection}" HorizontalAlignment="Left">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Rectangle Fill="{Binding Path=Brush}" Stroke="Black" StrokeThickness="1" Height="15" Width="30"/>
<w:WTextBlock Text="{Binding Name}" VerticalAlignment="Center"
Foreground="{StaticResource SmallControlForegroundBrush}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
SpeciesColorCollection is an ObservableCollection of type ColorObject
Public Class ColorObject
Public Property Name As String
Public Property Brush As Brush
End Class
The ComboBox displays the items from the collection correctly but my problem is when I try to get the selected text from the ComboBox in a MultiBinding, I receive ColorObject instead of the name. How do I go about getting the value of the "Name" from within the WTextBlock of the ComboBox? The binding that I am using for my command is below. The converter is only returning strings.
<MultiBinding Converter="{StaticResource mySpeciesSetupConverter}">
<MultiBinding.Bindings>
<Binding ElementName="NewSpeciesName" Path="Text" />
<Binding ElementName="ExistingSpeciesComboBox" Path="Text" />
</MultiBinding.Bindings>
</MultiBinding>
<MultiBinding Converter="{StaticResource mySpeciesSetupConverter}">
<MultiBinding.Bindings>
<Binding ElementName="NewSpeciesName" Path="Text" />
<Binding ElementName="ExistingSpeciesComboBox" Path="SelectedItem.Name" />
</MultiBinding.Bindings>
</MultiBinding>
I have a ListView in my XAML and I am trying to hook up a MultiBinding Converter.
<ListView
Grid.Column="4"
Grid.Row="1"
Grid.RowSpan="5"
Margin="8,0,8,8"
HorizontalAlignment="Stretch"
Name="lvDisplayType"
ItemsSource="{Binding Path=Types}"
SelectedItem="{Binding Path=Current.Opt}"
VerticalAlignment="Stretch"
SelectionChanged="lvType_SelectionChanged"
SelectionMode="Single"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListView.ItemTemplate>
<DataTemplate>
<DockPanel
HorizontalAlignment="Center">
<TextBlock
Text="{Binding Path=., Converter={StaticResource DisplayConverter}}"
HorizontalAlignment="Center"
Padding="6"
VerticalAlignment="Center"
TextWrapping="Wrap">
<TextBlock.ToolTip>
<ToolTip DataContext="{Binding Path=Current}">
<MultiBinding Converter="{StaticResource OptConverter}">
<Binding Path="Opt" />
<Binding Path="Type" />
</MultiBinding>
</ToolTip>
</TextBlock.ToolTip>
</TextBlock>
</DockPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The code not working is:
<TextBlock.ToolTip>
<ToolTip DataContext="{Binding Path=Current}">
<MultiBinding Converter="{StaticResource OptConverter}">
<Binding Path="Opt" />
<Binding Path="Type" />
</MultiBinding>
</ToolTip>
</TextBlock.ToolTip>
At present the Converter is returning an empty string as both 'values[0] == System.Windows.DependencyProperty.UnsetValue' and 'values[1] == System.Windows.DependencyProperty.UnsetValue' return true. These values are never set.
Because of the logical tree (I think) the TextBlock.ToolTip default binding is 'Current.Opt'. For the MultiBinding I also need to refer to 'Type' which is another property of 'Current'. So to get around this I have set 'ToolTip DataContext="{Binding Path=Current}"' - this isn't working as expected - what am I doing wrong?
I know I could do this easily in the Code behind, but we are employing MVVM, so would like to avoid it if possible.
Any help greatly appreciated!
Thank you
Try to do it in this way,
1.Does this give DependencyProperty.UnsetValue in the Converter? Otherwise, what is coming in to the converter?
<TextBlock.ToolTip>
<MultiBinding Converter="{StaticResource OptConverter}">
<Binding RelativeSource="{RelativeSource Self}" />
<Binding RelativeSource="{RelativeSource Self}" />
</MultiBinding>
</TextBlock.ToolTip>
2.Does this give DependencyProperty.UnsetValue in the Converter?
<TextBlock.ToolTip>
<MultiBinding Converter="{StaticResource OptConverter}">
<Binding RelativeSource="{RelativeSource Self}" Path="Current"/>
<Binding RelativeSource="{RelativeSource Self}" Path="Current"/>
</MultiBinding>
</TextBlock.ToolTip>
I define a headertemplate into a wpf groupbox and the databinding doesn't work. I don't understand why.
<GroupBox>
<GroupBox.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" >
<Image Source="/PopuAssuNetApplication.UI.Control;component/Images/Members.png" Width="24" />
<TextBlock VerticalAlignment="Center">
<TextBlock.Text>
<MultiBinding StringFormat="{x:Static Member=resx:Resources.PersonsInContractGroupBox}">
<Binding Path="CurrentContract.Federation" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type GroupBox}}">
</Binding>
<Binding Path="CurrentContract.Type" Converter="{StaticResource contractTypeConverter}" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type GroupBox}}">
</Binding>
<Binding Path="CurrentContract.Number" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type GroupBox}}">
</Binding>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<WpfComponent:WaitControl Margin="7,0,0,0" VerticalAlignment="Top" Width="24" Height="24" MarginCenter="4">
<WpfComponent:WaitControl.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsMembersOfContractBusy, UpdateSourceTrigger=PropertyChanged, ElementName=PersonsInContract}" Value="true">
<Setter Property="WpfComponent:WaitControl.Visibility" Value="Visible" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=IsMembersOfContractBusy, UpdateSourceTrigger=PropertyChanged, ElementName=PersonsInContract}" Value="false">
<Setter Property="WpfComponent:WaitControl.Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</WpfComponent:WaitControl.Style>
</WpfComponent:WaitControl>
</StackPanel>
</DataTemplate>
</GroupBox.HeaderTemplate>
The problem is that the HeaderTemplate is used for templating the Header thus within the HeaderTemplate your DataContext is whatever you bind or assign to the Header property of your GroupBox.
Think of the Header property as almost like the DataContext for the header of the control. Normally the DataContext property inherits its value from its parent but since not every control has a Header the Header is blank unless you set it.
By binding your Header explicitly to the current DataContext Header="{Binding}" your example should work as you expect. To help illustrate how this works I've created a simple example below that shows how the Header and DataContext work independently from each other for providing data to either the body or header of the control.
<GroupBox Header="HEADER TEXT" DataContext="BODY TEXT">
<GroupBox.HeaderTemplate>
<DataTemplate>
<Button Content="{Binding}"
Background="LightGreen" />
</DataTemplate>
</GroupBox.HeaderTemplate>
<CheckBox HorizontalAlignment="Center"
VerticalAlignment="Center" Content="{Binding}" />
</GroupBox>
This will yield a GroupBox that looks like the following.
I think that by default in databinding, wpf always gets data from the DataContext property. Seems not in datatemplate
Your assumption is correct about DataContext and it does work in the DataTemplate as I've demonstrated it's just that in the Header's template the DataContext is the value from the Header Property and not the DataContext itself.
The GroupBox does not have a member called "CurrentContract". Most probably, you want to accesss a property called "CurrentContract" from the corresponding ViewModel?! The ViewModel is the GroupBox's DataContext, so you have to change the Binding Paths to something like...
<Binding Path="DataContext.CurrentContract.Type" Converter="{StaticResource contractTypeConverter}" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type GroupBox}}">
<GroupBox >
<GroupBox.HeaderTemplate>
<DataTemplate>
<RadioButton Content="myR"
IsChecked="{Binding rIsChecked, Mode=TwoWay}"
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type GroupBox}}}" />
</DataTemplate>
</GroupBox.HeaderTemplate>
<GroupBox.Content>
<Grid IsEnabled="{Binding rIsChecked}">
</Grid>
</GroupBox.Content>
</GroupBox>
Just propagate the GroupBox DC to the DataTemplate content...works like a charm...
The lesson learned above is useful in general for DataTemplates, but I actually found out recently there is a better way to change the header of a groupbox:
<GroupBox>
<GroupBox.Header>
<CheckBox IsChecked="{Binding Path=mSomeBoolean}"/>
</GroupBox.Header>
</GroupBox>
This way there is no need to define a relative source in the bindings.
Also please note this issue with GroupBoxes and the header.
This is what worked for me:
<HeaderedContentControl Header="{Binding}" Style="{StaticResource TallHeaderedContentStyle}">
<HeaderedContentControl.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=HeaderText"} />
</DataTemplate>
</HeaderedContentControl.HeaderTemplate>