Do Style Setters support two way bindings (for attached properties)? - wpf

Here's my XAML:
<DataGridTemplateColumn Width="*"
CanUserResize="True"
CanUserSort="True"
Header=" Заголовок "
SortMemberPath=".">
<DataGridTemplateColumn.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="Helpers:FocusHelper.IsFocused" Value="{Binding IsEnvelopeFocused, Mode=TwoWay}"/>
</Style>
</DataGridTemplateColumn.CellStyle>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Name="txtTitle" VerticalAlignment="Center">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource TitleConverter}" UpdateSourceTrigger="PropertyChanged">
<Binding Path="." />
<Binding Path="DataContext.Language" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=UserControl}" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
FocusHelper starts listening for the changes of IsFocused property. But passing the value to IsEnvelopeFocused happens only once.
I cannot find the cause of this behavior.

Yes, the do.
The problem was in the binding but I have no idea why.

Related

Binding Combobox does not work when a Converter is used

I'm trying to bind a ComboBox to DataContext.
<ComboBox ItemsSource="{Binding Path=Numbers}"
SelectedValue="{Binding Path=CurrentNumber,Mode=TwoWay}">
</ComboBox>
The above code works, but when I try to change how the items are displayed using a converter implementing IMultiValueConverter and MultiBinding nothing is displayed. I have debugged the method implementing the IMultiValueConverter and it is not getting executed. What could be the problem?
<ComboBox ItemsSource="{Binding Path=Numbers}"
SelectedValue="{Binding Path=CurrentNumber,Mode=TwoWay}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource MultiUnitConverter}" ConverterParameter="{x:Static enumerations:Quantity.Length}" >
<Binding Path="."/>
<Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type Window}}" Path="DataContext.CurrentUnit"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Update:
I tried the following instead of the ComboBox, the converter is fired and the data is loaded but not displayed!
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource MultiUnitConverter}" ConverterParameter="{x:Static enumerations:Quantity.Length}" >
<Binding Path="CurrentNumber"/>
<Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type Window}}" Path="DataContext.CurrentUnit"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
The following works though:
<TextBlock>
<TextBlock.Text>
<Binding Path="CurrentNumber"></Binding>
</TextBlock.Text>
</TextBlock>
For all who may get stuck with this in the future and ruin their entire evening here is the solution I found!
It seems adding StringFormat solves the problem!
<ComboBox ItemsSource="{Binding Path=Numbers}" SelectedItem="{Binding Path=Number, Mode=TwoWay}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock>
<TextBlock.Text>
<MultiBinding
Converter="{StaticResource MultiUnitConverter}"
ConverterParameter="{x:Static enumerations:Quantity.Length}"
StringFormat="{}{0:0.###}">
<Binding Path="."/>
<Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type Window}}" Path="DataContext.CurrentUnit"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Did you define the converter resource somewhere else in your xaml? If not, you should do so. For instance, if your ComboBox lives in a UserControl you could add:
<UserControl.Resources>
<local:MultiUnitConverter x:Key="multiUnitConverter"/>
</UserControl.Resources>
And of course you would need to update your Converter StaticResource to match the case-sensitive Key above.

WPF CheckBox UnCheck Event Not Firing

I have a checkbox inside an ItemsControl. It has an EventTrigger both for Checked and UnChecked events. It is also bound to a Command with multiple CommandParameters. The Checked event triggers just fine.
However, the UnChecked event does not fire. What am I missing?
<ItemsControl Grid.Column ="2"
Grid.Row ="0"
ItemsSource ="{Binding ParameterOptionGroup.ParameterOptions}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding State, Converter={StaticResource BoolToEntityState}, Mode=OneWay}">
<trigger:Interaction.Triggers>
<trigger:EventTrigger EventName="Checked">
<trigger:InvokeCommandAction Command ="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}},
Path=DataContext.InitiateParameterAnswer}">
<trigger:InvokeCommandAction.CommandParameter>
<MultiBinding Converter="{StaticResource GuidAndParameterOptionToTuple}">
<Binding Path="DataContext" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type CheckBox}}"></Binding>
<Binding Path="DataContext.Id" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}"></Binding>
<Binding Path="Text" ElementName="txtRemarks" />
<Binding Source="True" />
<Binding Source="10" />
</MultiBinding>
</trigger:InvokeCommandAction.CommandParameter>
</trigger:InvokeCommandAction>
</trigger:EventTrigger>
</trigger:Interaction.Triggers>
<trigger:EventTrigger EventName="UnChecked">
<trigger:InvokeCommandAction Command ="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}},
Path=DataContext.InitiateParameterAnswer}">
<trigger:InvokeCommandAction.CommandParameter>
<MultiBinding Converter="{StaticResource GuidAndParameterOptionToTuple}">
<Binding Path="DataContext" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type CheckBox}}"></Binding>
<Binding Path="DataContext.Id" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}"></Binding>
<Binding Path="Text" ElementName="txtRemarks" />
<Binding Source="False" />
<Binding Source="5" />
</MultiBinding>
</trigger:InvokeCommandAction.CommandParameter>
</trigger:InvokeCommandAction>
</trigger:EventTrigger>
</CheckBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
As #Bort pointed out, the event name should be Unchecked instead of UnChecked.

XAML Tool Tip Data Context and Multi Binding

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>

WPF GroupBox HeaderTemplate and DataBinding

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>

Does ItemsTemplate+DataTemplate obscure bindings?

At several points in my current application, I have an ItemTemplate such as the following:
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ToggleButton Margin="2,0" Content="{Binding}" Tag="{Binding}" Click="ToggleButton_Click">
<ToggleButton.IsChecked>
<MultiBinding Mode="OneWay" Converter="{StaticResource EqualityConverter}">
<Binding RelativeSource="{RelativeSource Self}" Path="Tag">
</Binding>
<Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type cc:ToggleButtonPanel}}" Path="SelectedItem">
</Binding>
</MultiBinding>
</ToggleButton.IsChecked>
</ToggleButton>
</DataTemplate>
</ItemsControl.ItemTemplate>
Now, I've stepped through the converter and it returns true and false as expected, but the controls do not change their ischecked state... There are no binding errors, and this only happens when using an ItemsTemplate/DataTemplate combo. Has anyone else seen this behaviour?

Resources