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>
Related
This question already has answers here:
Binding ConverterParameter
(3 answers)
Closed 1 year ago.
It seems it is very common to pass a static value to the ConverterParameter like the xaml example below.
Is there way to pass the parent control Tag property to it?
<StackPanel Margin="{StaticResource XSmallTopMargin}" Tag="abc">
<RadioButton GroupName="AppTheme" dt:DesignTime.Content="Light"
Checked="ThemeChanged_CheckedAsync"
IsChecked="{x:Bind ElementTheme, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter=Light, Mode=OneWay}">
</RadioButton>
</StackPanel>
You can't. At least not that I'm aware. It would require you to bind it and there's no way to bind anything to ConverterParameter. x:Static and StaticResource are fine ways to pass something to ConverterParameter. But Binding won't work
However what you could do is also make your converter also support IMultiValueConverter and then pass the parent tag in a MultiBinding.
I'm probably about to butcher your binding but it's roughly like this.
<RadioButton GroupName="AppTheme" dt:DesignTime.Content="Light"
Checked="ThemeChanged_CheckedAsync" >
<RadioButton.IsChecked>
<MultiBinding Converter="{StaticResource EnumToBooleanConverter}">
<Binding Source="{x:Bind ElementTheme}"/>
<Binding RelativeSource="{RelativeSource Self}" Path="Tag"/>
</MultiBinding>
</RadioButton.IsChecked>
</RadioButton>
I'm trying to use multibinding on a context menu stored in Window.Resources and I can't figure out how to get the SelectedItem of the ListBox I attach the menu to.
I think I need to use PlacementTarget, but can't get anything to work. I tried using FindAncestor, but that didn't work. I also tried getting the listbox by the ElementName to no avail.
<Window ...>
<Window.DataContext>
<local:IPViewModel/>
</Window.DataContext>
<Window.Resources>
<sys:Int32 x:Key="Number">0</sys:Int32>
<sys:Int32 x:Key="NumberLetter">1</sys:Int32>
<sys:Int32 x:Key="NumberLetterNumber">2</sys:Int32>
<local:CombineParams x:Key="CombineParams"/>
<ContextMenu x:Key="DetailMenu">
<MenuItem Header = "Number">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:InvokeCommandAction Command="{Binding SwitchLabelMode}">
<i:InvokeCommandAction.CommandParameter>
<MultiBinding Converter="{StaticResource CombineParams}">
<!-- The next line is where I'm not sure what to write to get to the listbox -->
<Binding Source="{RelativeSource AncestorType={x:Type ListBox}}"/>
<Binding Source="{StaticResource Number}"/>
</MultiBinding>
</i:InvokeCommandAction.CommandParameter>
</i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</MenuItem>
...
</Window.Resources>
...
<ListBox Name="IndexDetailsListBox"
...
ContextMenu="{StaticResource DetailMenu}"
...>
</ListBox>
...
</Window>
I can't get it to give back anything other than unset value.
I'm grateful for any help. None of the other questions I found have the contextmenu in Window.Resources with MultiBinding which seems to be my issue.
Your problem is, that you assign RelativeSource- object to the Source property of the Binding object. RelativeSource- object belongs to the RelativeSource-property.
So you get your List to the multi value converter:
<MultiBinding Converter="{StaticResource CombineParams}">
<Binding Path="PlacementTarget" RelativeSource="{RelativeSource AncestorType={x:Type ContextMenu}}"/>
<Binding Source="{StaticResource Number}"/>
</MultiBinding>
I have a rookie question, but I couldn't find an answer for that. I'm not even sure how to ask google that question.
Let's say you have:
Tag="{Binding DataContext, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type StackPanel}, AncestorLevel=2}}"
How to do it this way?:
<Button.Tag>
<Binding ??? RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type StackPanel}, AncestorLevel=2}" />
</Button.Tag>
I've tried "Content" "Value" "Source".. Thanks for help!
<Button ...>
<Button.Tag>
<Binding .../>
</Button.Tag>
</Button>
is equivalent to
<Button Tag="{Binding ...}" />
However, you are missing the Binding's source property path. Add Path="DataContext":
<Button.Tag>
<Binding Path="DataContext" .../>
</Button.Tag>
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.
I need to create a DisplayMemberPath that is a compound of a few properties (ie object.category.Name+" -> "+object.description) I'm pretty sure I can do this by creating a dynamic data type that encapsulates the object and also adds a new property called displayField that is what I need but I'm wondering if there is a more proper way to do this that does not involve creating a new object. Any ideas?
DisplayMemberPath is just a "shortcut" for when you don't need a complex template for items. If you need more control, use ItemTemplate instead:
<ComboBox ItemsSource="{Binding Items}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} -> {1}">
<Binding Path="Category.Name" />
<Binding Path="Description" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>