如何使用多个依赖属性绑定到计算 属性

How to bind to calculated property using multiple dependency properties

我正在尝试构建一个可以在整个项目中重复使用的 WPF UserControl。基本上是这样的:

当window调整大小太小时,我只显示底线以保存space。所以我想要这些选项:

我正在使用三个依赖属性间接设置每一行的可见性:

直接绑定到依赖属性有效。间接没有。

现在的问题是我是否需要使用:

  1. FrameworkPropertyMetadata 的 FrameworkPropertyMetadataOptions.AffectsRender 等标志
  2. 为 DependencyProperty.Register
  3. 创建 FrameworkPropertyMetadata 时的 PropertyChangedCallback
  4. 一个值转换器
  5. INotifyPropertyChanged

我试过的

我尝试了以上所有四个选项,但我对它的理解还不够深入,无法让其中一个起作用。

我创建了普通属性,return 是三个依赖属性的计算值。这些 使用 DependencyProperty.Register 设置的默认值,它们不更新甚至不使用 parent 用户控件中设置的值。

当前状态

<StackPanel>
    <Border Height="1"
            Background="Black"
            Margin="0 10 0 0"
            Visibility="{Binding ShowTopLine, ElementName=Root, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}"/>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <TextBlock Grid.Column="0"
                   Text="{Binding Text, ElementName=Root, Mode=TwoWay, FallbackValue=Heading}"
                   Margin="0 10 10 0" />
        <Border Grid.Column="1" 
                Height="1" 
                Margin="0 10 0 0" 
                Background="Black" 
                VerticalAlignment="Center" 
                Visibility="{Binding ShowBottomLine, ElementName=Root, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}"/>
    </Grid>
</StackPanel>

code-behind 的相关部分,显示三个依赖属性之一:

internal partial class Heading
{
  public bool ShowBottomLine => ShowLine && (!CanCompress || ShowCompressed);

  public static readonly DependencyProperty CanCompressProperty = GetRegisterProperty("CanCompress", typeof(bool), true);

  public bool CanCompress
  {
    get => (bool) GetValue(CanCompressProperty);
    set => SetValue(CanCompressProperty, value);
  }

  // Same for other two properties

  public static DependencyProperty GetRegisterProperty(string name, Type type, object defaultValue)
  {
    return DependencyProperty.Register(name, type, typeof(Heading), new FrameworkPropertyMetadata(defaultValue));
  }
}

我如何在另一个用户控件中使用标题:

<local:Heading Text="{Binding HeaderText}"
                                   CanCompress="False" 
                                   ShowLine="False"/>

我该如何继续?我知道放弃计算属性很简单,但这意味着我需要在其他地方计算它们的状态。我想在标题中全部完成。

主要问题似乎是计算的属性不会强制刷新。这是一个公平的总结吗?

您可以通过定义 ControlTemplate 和简单的触发器来实现此逻辑:

<UserControl x:Class="ExampleControl">
  <UserControl.Template>
    <ControlTemplate TargetType="ExampleControl">

      <StackPanel>
        <Border x:Name="TopLine"
                Background="Black" 
                Height="1"
                Margin="0 10 0 0" />
        <Grid>
          <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition />
          </Grid.ColumnDefinitions>
          <TextBlock Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Text, FallbackValue=Heading}"
                     Margin="0 10 10 0" />
          <Border x:Name="BottomLine"
                  Grid.Column="1"
                  Height="1"
                  Margin="0 10 0 0"
                  Background="Black"
                  VerticalAlignment="Center" />
        </Grid>
      </StackPanel>

      <ControlTemplate.Triggers>
        <Trigger Property="ShowLines" Value="False">
          <Setter TargetName="TopLine" Property="Visibility" Value="Collapsed" />
          <Setter TargetName="BottomLine" Property="Visibility" Value="Collapsed" />
        </Trigger>

        <MultiTrigger>
          <MultiTrigger.Conditions>
            <Condition Property="CanCompress" Value="True" />
            <Condition Property="ShowCompressed" Value="True" />
          </MultiTrigger.Conditions>
          <Setter TargetName="TopLine"
                  Property="Visibility"
                  Value="Collapsed" />
        </MultiTrigger>

        <MultiTrigger>
          <MultiTrigger.Conditions>
            <Condition Property="CanCompress" Value="True" />
            <Condition Property="ShowCompressed" Value="False" />
          </MultiTrigger.Conditions>
          <Setter TargetName="BottomLine"
                  Property="Visibility"
                  Value="Collapsed" />
        </MultiTrigger>
      </ControlTemplate.Triggers>
    </ControlTemplate>
  </UserControl.Template>
</UserControl>

或者(推荐)使用自定义控件 (extend Control)。