简体   繁体   English

WPF - 具有多重绑定和不同数据上下文的自定义工具提示

[英]WPF - custom ToolTip with MultiBinding and different DataContexts

I want to make a UserControl in WPF (C# - MVVM) with a custom two-lines ToolTip .我想用自定义的两行ToolTip在 WPF (C# - MVVM) 中创建一个UserControl

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 .在视图中,我有一个带有ItemSourceListBox和一个自定义ItemTemplate用于设置在运行时仅显示第一行而第二行是空string的前一个ToolTip Indeed the problem is the second line of the ToolTip where I use a MultiBinding with a converter;确实,问题出在ToolTip的第二行,我在其中使用了带有转换器的MultiBinding converter that fails in try/catch returning an empty string .try/catch中失败的转换器返回空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.我知道异常是由一个null值生成的,而它应该是一个不可为nullint ,但我不明白为什么。
EDIT: I was wrong saying null ;编辑:我说null是错误的; the problem is that the converter strikes a cast exception because of DependencyProperty UnsetValue , I don't know why.问题是转换器由于DependencyProperty UnsetValue转换异常,我不知道为什么。

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:这里的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>
...

As you can see Value is a property of an object in ObservableCollection Values .如您所见, ValueObservableCollection Values对象的属性。 Decimals and Values are properties in code behind associated to their dependency properties. DecimalsValues是与它们的依赖属性相关联的代码中的属性。

Here Decimals definition:这里的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); }
}

I don't understand why for the TextBlock outside the ToolTip it works and why inside the ToolTip not.我不明白为什么在ToolTip之外的TextBlock可以工作,为什么在ToolTip内部不行。 How can I resolve the problem?我该如何解决问题?

The binding fails because the UserControl is not a visual ancestor of the ToolTip .绑定失败,因为UserControl不是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 :您可以在绑定Tag的财产GridDecimals属性,然后绑定到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>

How I have solved:我是如何解决的:
I have read the comment of @Sinatr about BindingProxy and finally I have found how to avoid the problem.我已经阅读了@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>
...

In this case the BindingProxy is binded directly to the DependencyProperty Decimals and not to DataContext .在这种情况下, BindingProxy直接绑定到DependencyProperty Decimals而不是DataContext

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM