WPF - настраиваемая подсказка с MultiBinding и различными DataContexts - PullRequest
1 голос
/ 24 января 2020

Я хочу сделать UserControl в WPF (C# - MVVM) с пользовательскими двумя строками ToolTip.

В представлении у меня есть ListBox с ItemSource и пользовательский ItemTemplate, где установить предыдущий ToolTip, который во время выполнения показывает только первую строку, а вторая - пустую string. На самом деле проблема во второй строке ToolTip, где я использую MultiBinding с конвертером; преобразователь, который не работает в try/catch и возвращает пустое значение string.

Я знаю, что исключение генерируется значением, равным null, в то время как оно должно быть int не обнуляемым, но Я не понимаю, почему.
РЕДАКТИРОВАТЬ: Я был неправ, говоря null; проблема в том, что преобразователь выдает исключение приведения из-за DependencyProperty UnsetValue, я не знаю почему.

Здесь Преобразователь код:

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();
    }
}

Здесь XAML код:

...
<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>
...

Как видите, Value является свойством объекта в ObservableCollection Values. Decimals и Values - это свойства кода, связанные с их свойствами зависимостей.

Здесь Decimals определение:

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); }
}

Я не понимаю, почему для TextBlock снаружи ToolTip работает, а почему внутри ToolTip нет. Как я могу решить проблему?

Ответы [ 2 ]

1 голос
/ 24 января 2020

Привязка завершается неудачно, поскольку UserControl не является визуальным предком ToolTip.

. Вы можете связать свойство Tag Grid со свойством Decimals, а затем связать в свойство Tag с использованием свойства PlacementTarget 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>
0 голосов
/ 27 января 2020

Как я решил:
Я прочитал комментарий @Sinatr о BindingProxy и, наконец, я нашел, как избежать этой проблемы.

<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>
...

В этом случае BindingProxy привязывается непосредственно к DependencyProperty Decimals, а не к DataContext.

...