How to bind to Popup ActualHeight - c#

I have a grid that I want a popup to show in a constant relation to it regardless of the size of both the popup and the grid. I use a converter for it here is the code
< Grid Name=YParamTextBlock>
<TextBlock HorizontalAlignment="Center"/>
<Popup PlacementTarget="{Binding ElementName=YParamTextBlock}} Placement="Center">
<Popup.VerticalOffSet>
<MultiBinding Mode="OneWay" Converter="{StaticResource OffsetConverter} NotifyOnTargetChanged="True">
<Binding Mode="OneWay" ElementName="YParamTextBlock" Path="ActualHeight" NotifyOnTargetUpdated="True"/>
<Binding Mode="OneWay" RelativeSource={RelativeSource Self} Path="ActualHeight" NotifyOnTargetChanged="True"/>
</MultiBinding>
</Popup.VerticalOffset>
</Popup>
<Grid>
The problem is that the actual height is 0.0 for the two controls when they are first created, so I added the NotifyOnTargetChanged in order to fix it.
Now, for some reason the NotifyOnTargetChanged fixed the rebinding for the Grid's ActualHeight, but the Popup is still 0.0. Is there anyway to notify the popup actual height has changed? Or any other solution for this issue?

Actual Height and Width are read-only you cannot bind directly, you can use the solution explained by Kent Boogaart in this Answer

Why the multibinding? VerticalOffset is a double so you just need one binding value.
I don't know what your Converter does, but assuming you want it to take the ActualHeight property of your YParamTextBlock grid and then return a double corresponding to the VerticalOffset you want to give to your popup then the following is probably easier to follow:
<Grid x:Name="YParamTextBlock">
<TextBlock HorizontalAlignment="Center"/>
<Popup PlacementTarget="{Binding ElementName=YParamTextBlock}}
Placement="Center"
VerticalOffset="{Binding ActualHeight, ElementName=YParamTextBlock,
Converter={StaticResource OffsetConverter}}"/>
<Grid>

Related

Passing incorrect values into MultiValueConverter by MultiBinding

I've been trying to implement a dynamic ToolTip (WPF) for RadioButton (the ToolTip switches, when IsEnabled of the RadioButton changes). I wanted to achieve this with a MultiValueConverter, which would be sort of a general Converter, that accepts 3 values - the IsEnabled value, enabled ToolTip and disabled ToolTip in this exact order.
But sadly I have encountered an issue, that I haven't been able to solve yet. Basically when the code reaches the Convert Method, the Array of values is filled with DependencyProperty.UnsetValue items.
What I managed to find while googling was, that the problem is propably caused by having a wrong DataContext as mentioned here WPF MultiBinding in Convertor fails ==> DependencyProperty.UnsetValue , but I feel like I have tried every combination of RelativeSources and DataContexts, that I could come up with and nothing helped.
Here is the sample code of the View:
<window.Resources>
<local:BooleanToStringConverter x:Key="BooleanToStringConverter"/>
</window.Resources>
<Grid>
<RadioButton x:Name="RadioButton" ToolTipService.ShowOnDisabled="True"
IsEnabled="{Binding IsRadioButtonEnabled}" VerticalAlignment="Center" HorizontalAlignment="Center" Content="Radio">
<RadioButton.ToolTip>
<ToolTip>
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource BooleanToStringConverter}">
<Binding ElementName="RadioButton" Path="IsEnabled"/>
<Binding RelativeSource="{RelativeSource AncestorType={x:Type local:MainWindow}}" Path="ViewModel.EnabledToolTip"/>
<Binding RelativeSource="{RelativeSource AncestorType={x:Type local:MainWindow}}" Path="ViewModel.DisabledToolTip"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</ToolTip>
</RadioButton.ToolTip>
</RadioButton>
So the result, that I expect from this, is that the correct values will be passed into the Converter (in this case value of IsEnabled and string values of Enabled/Disabled ToolTips).
If anybody has any ideas, I would very much appreciate to hear them :).
Thanks in advance.
I managed to fix this by explicitly setting DataContext on the RadioButton and removing the RelativeSources within the MultiBinding. Though I don't understand why it did not work with the RelativeSources, it works. Here is the code, in case of anyone reading this in the future:
<RadioButton x:Name="RadioButton" ToolTipService.ShowOnDisabled="True"
DataContext="{Binding ViewModel, RelativeSource={RelativeSource AncestorType={x:Type local:MainWindow}}}"
IsEnabled="{Binding IsRadioButtonEnabled}" VerticalAlignment="Center" HorizontalAlignment="Center" Content="Radio">
<RadioButton.ToolTip>
<ToolTip>
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource BooleanToStringConverter}">
<Binding Path="IsRadioButtonEnabled"/>
<Binding Path="EnabledToolTip"/>
<Binding Path="DisabledToolTip"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</ToolTip>
</RadioButton.ToolTip>
</RadioButton>

Force RadioButtons to only change if the bound value actually changes

I have a bit of a strange case. The issue is that I have 3 radiobuttons and they are binded to the EffectiveValue property of my viewmodel.
<StackPanel Grid.Column="2" Orientation="Horizontal" Margin="5,0,0,0">
<StackPanel.Visibility>
<MultiBinding Converter="{StaticResource MultiBoolToVisibilityHidden}">
<Binding Path="IsSelected" RelativeSource="{RelativeSource AncestorType=telerik:RadListBoxItem}"/>
<Binding Path="Criterion.IsOverridable"/>
<Binding Path="DataContext.CanOverrideEvaluation" ElementName="ThisInformationControl"/>
</MultiBinding>
</StackPanel.Visibility>
<telerik:RadRadioButton Content="Y" IsChecked="{Binding EffectiveValue, Converter={StaticResource EnumToBool}, ConverterParameter={x:Static utilities:Ternary.TRUE}, Mode=OneWay}" Style="{StaticResource EmptyRadioButtonStyle}"
Command="{Binding OverrideValueCommand}" CommandParameter="{x:Static utilities:Ternary.TRUE}" Margin="2,1" Width="16" VerticalAlignment="Center"/>
<telerik:RadRadioButton Content="?" IsChecked="{Binding EffectiveValue, Converter={StaticResource EnumToBool}, ConverterParameter={x:Static utilities:Ternary.UNKNOWN}, Mode=OneWay}" Style="{StaticResource EmptyRadioButtonStyle}"
Command="{Binding OverrideValueCommand}" CommandParameter="{x:Static utilities:Ternary.UNKNOWN}" Margin="2,1" Width="16" VerticalAlignment="Center"/>
<telerik:RadRadioButton Content="N" IsChecked="{Binding EffectiveValue, Converter={StaticResource EnumToBool}, ConverterParameter={x:Static utilities:Ternary.FALSE}, Mode=OneWay}" Style="{StaticResource EmptyRadioButtonStyle}"
Command="{Binding OverrideValueCommand}" CommandParameter="{x:Static utilities:Ternary.FALSE}" Margin="2,1" Width="16" VerticalAlignment="Center"/>
</StackPanel>
The problem is this. Say my ViewModel Effective Value is 'Y'. I click the 'N' button. But then based on certain conditions, I do not actually set the EffectiveValue to 'N' and it remains 'Y'. The problem I am having is now the 'N' looks selected even though the viewmodel still says that the value is 'Y'. What can I do to make sure my radiobuttons strictly listen to the viewmodel's value?
My current workaround it to force a property changed on the effective value but I feel this is kind of a hack.
Thanks
I think the answer lies with your radio button bindings. Notice that you have them set to OneWay, but you said that based on certain conditions you keep the value as 'Y'. How does the WPF control know that you've rejected the UI change?
I suggest changing the binding Mode to TwoWay and maybe also include UpdateSourceTrigger=PropertyChanged in the bindings. Then your code behind and the UI can communicate "two ways" to each other about what the appropriate state of the radio button should be.

Updating the source of a Listbox with a multibinding

I'm trying to get a multi-bound listbox to call the convert back method when items inside it change. Specifically I have a datatemplate that maps each entry to a checkbox and I'd like the source to be updated whenever a box is checked.
<ListBox x:Name="listBoxEdit" Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="3" Height="100" >
<ListBox.ItemsSource>
<MultiBinding Converter="{StaticResource selectedLoadsConverter}" >
<Binding Path="AvailableLoads"/>
<Binding Path="CurrentUnit"/>
</MultiBinding>
</ListBox.ItemsSource>
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Item1}" IsChecked="{Binding Item2}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I know the problem lies in the fact that I'm binding to to the ItemsSource property which is never actually being updated by the checkbox changing, but I can't figure out a good way to push the updates up to the source.

Add brackets to text box value

I want to know if there is option that when I type into a text box or drag to anything (I'm using D&D functionality), the text on it will automatically insert brackets. I don't want to do that on the logic or in the code beyond just in the ui. Is that posible?
For example: if I type AAA, I will see in the text box (AAA).
I dont want to do that on the logic or in the code beyond
By your conditions it is not possible. Something has to capture the change event and add the brackets to the text. That something is not possible without logic as found in code behind.
The options are
Subscribe to the textblock's SelectionChange event and add the brackets.
Create a custom control which does #1 internal so the consumer doesn't have to do it. (By a technicality it answer's your question).
Put the textblock control between two labels which have the brackets as their context. Bind their visibility to a Boolean on the VM which reports when the bound data of the textblock has changed. If there is text then they become visible, if there is no text it is hidden. Downside is that this is not caught as the user types or until it fully changed, only when exiting the control.
Here is #3
<Label Content="(" Visibility="{Binding HasText, Converter={StaticResource WindowsVisibilityBooleanConverter}}" />
<TextBox Text="{Binding TextInput}"
Height="18"
HorizontalAlignment="Stretch" />
<Label Content=")" Visibility="{Binding HasText, Converter={StaticResource WindowsVisibilityBooleanConverter}}" />
Without any of your own logic code, I suppose this is the closest thing to what you want.
<TextBox x:Name="tbInput" />
<TextBlock Text="{Binding ElementName='tbInput', Path=Text, StringFormat={}({0})}" />
The downside would be that you'll always see the empty brackets () if the TextBox is empty.
See: String format using MultiBinding?
<StackPanel>
<Slider x:Name="sl1" Minimum="10" Maximum="100"/>
<Slider x:Name="sl2" Minimum="10" Maximum="100"/>
<Label x:Name="label13" Background="Yellow" Foreground="Black">
<Label.Content>
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} x {1} Test">
<Binding ElementName="sl1" Path="Value" />
<Binding ElementName="sl2" Path="Value" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</Label.Content>
</Label>
</StackPanel>

Multibinding using Window's DataContext

I'm trying to create drag adorner based on whether the Customer DependancyProperty of a Window is null. I have this in the Window's resources. The first part of the binding is set (the item being dragged), but the second (the DependancyProperty on the WIndow) shows as UnsetValue.
The property is definitely initialized as it's used as the window's datacontext.
<Window x:Name="root"
...
>
<Window.Resources>
<DataTemplate x:Key="DragAdorner">
<StackPanel Orientation="Horizontal">
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource vehicleDragConverter}">
<Binding/>
<Binding Path="Customer" ElementName="root"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</StackPanel>
</DataTemplate>
customer is not a direct property of element window.
use <Binding Path="DataContext.Customer"..../>
or you can also use RelativeSource=FindControl Window in binding.

Categories