Reputation: 21
I want to make a UserControl
in WPF (C# - MVVM) with a custom two-lines ToolTip
.
In the View I have a ListBox
with an ItemSource
and a custom ItemTemplate
where to set the previous ToolTip
that at runtime shows only the first line while the second one is an empty string
. Indeed the problem is the second line of the ToolTip
where I use a MultiBinding
with a converter; converter that fails in try/catch
returning an empty string
.
I know that the exception is generated by a value that is null
while it should be an int
not nullable, but I don't understand why.
EDIT: I was wrong saying null
; the problem is that the converter strikes a cast exception because of DependencyProperty UnsetValue
, I don't know why.
Here Converter code:
public class FromDecimal_MVConverter : Base_MVConverter
{
public override object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
try
{
// Default are 2 decimals
if (values.Length == 2)
{
int decimals;
switch ((int)values[1])
{
case int dec when dec < 0:
decimals = 0;
break;
case int dec when dec > 99:
decimals = 99;
break;
default:
decimals = (int)values[1];
break;
}
return ((decimal)values[0]).ToString("N" + decimals.ToString());
}
else
{
return ((decimal)values[0]).ToString("N2");
}
}
catch
{
return string.Empty;
}
}
public override object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Here XAML code:
...
<ListBox ItemsSource="{Binding Values, RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<ToolTipService.ToolTip>
<StackPanel>
<TextBlock Text="{Binding Description}"/>
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{cv_ToString:FromDecimal_MVConverter}">
<Binding Path="Value"/>
<Binding Path="Decimals" RelativeSource="{RelativeSource FindAncestor, AncestorType=UserControl}"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</StackPanel>
</ToolTipService.ToolTip>
<TextBlock Foreground="{Binding Foreground, RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}}">
<TextBlock.Text>
<MultiBinding Converter="{cv_ToString:FromDecimal_MVConverter}">
<Binding Path="Value"/>
<Binding Path="Decimals" RelativeSource="{RelativeSource FindAncestor, AncestorType=UserControl}"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
...
As you can see Value
is a property of an object in ObservableCollection Values
. Decimals
and Values
are properties in code behind associated to their dependency properties.
Here Decimals
definition:
public static readonly DependencyProperty DecimalsProperty = DependencyProperty.RegisterAttached("Decimals", typeof(int), typeof(ucMyUserControl), new FrameworkPropertyMetadata(2) { BindsTwoWayByDefault = true });
public int Decimals
{
get { return (int)GetValue(DecimalsProperty); }
set { SetValue(DecimalsProperty, value); }
}
I don't understand why for the TextBlock
outside the ToolTip
it works and why inside the ToolTip
not.
How can I resolve the problem?
Upvotes: 1
Views: 637
Reputation: 21
How I have solved:
I have read the comment of @Sinatr about BindingProxy and finally I have found how to avoid the problem.
<UserControl.Resources>
<local:BindingProxy x:Key="BP_Decimals" Data="{Binding Decimals, RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}}"/>
</UserControl.Resources>
...
<DataTemplate>
<Grid>
<Grid.ToolTip>
<StackPanel>
<TextBlock Text="{Binding Description}"/>
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{cv_ToString:FromDecimal_MVConverter}">
<Binding Path="Value"/>
<Binding Path="Data" Source="{StaticResource BP_Decimals}"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</StackPanel>
</Grid.ToolTip>
...
</Grid>
</DataTemplate>
...
In this case the BindingProxy
is binded directly to the DependencyProperty Decimals
and not to DataContext
.
Upvotes: 0
Reputation: 169150
The binding fails because the UserControl
is not a visual ancestor of the ToolTip
.
You could bind the Tag
property of the Grid
to the Decimals
property and then bind to the Tag
property using the PlacementTarget
property of the ToolTip
:
<DataTemplate>
<Grid Tag="{Binding Decimals, RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}}">
<Grid.ToolTip>
<ToolTip>
<StackPanel>
<TextBlock Text="{Binding Description}"/>
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{cv_ToString:FromDecimal_MVConverter}">
<Binding Path="Value"/>
<Binding Path="PlacementTarget.Tag" RelativeSource="{RelativeSource FindAncestor, AncestorType=ToolTip}"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</StackPanel>
</ToolTip>
</Grid.ToolTip>
...
</Grid>
</DataTemplate>
Upvotes: 1