如何使用多个依赖属性绑定到计算 属性
How to bind to calculated property using multiple dependency properties
我正在尝试构建一个可以在整个项目中重复使用的 WPF UserControl。基本上是这样的:
- 水平线(称为顶线)
- 文本块 - 水平线(底线)
当window调整大小太小时,我只显示底线以保存space。所以我想要这些选项:
- 隐藏所有行
- 只显示第一行
- 只显示底线
我正在使用三个依赖属性间接设置每一行的可见性:
- 显示线路(启用线路)
- 可以压缩(启用显示顶部或底部,取决于可用 space)
- 显示压缩(true 时显示底线,false 时显示顶线)
直接绑定到依赖属性有效。间接没有。
现在的问题是我是否需要使用:
- FrameworkPropertyMetadata 的 FrameworkPropertyMetadataOptions.AffectsRender 等标志
- 为 DependencyProperty.Register
创建 FrameworkPropertyMetadata 时的 PropertyChangedCallback
- 一个值转换器
- 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
)。
我正在尝试构建一个可以在整个项目中重复使用的 WPF UserControl。基本上是这样的:
- 水平线(称为顶线)
- 文本块 - 水平线(底线)
当window调整大小太小时,我只显示底线以保存space。所以我想要这些选项:
- 隐藏所有行
- 只显示第一行
- 只显示底线
我正在使用三个依赖属性间接设置每一行的可见性:
- 显示线路(启用线路)
- 可以压缩(启用显示顶部或底部,取决于可用 space)
- 显示压缩(true 时显示底线,false 时显示顶线)
直接绑定到依赖属性有效。间接没有。
现在的问题是我是否需要使用:
- FrameworkPropertyMetadata 的 FrameworkPropertyMetadataOptions.AffectsRender 等标志
- 为 DependencyProperty.Register 创建 FrameworkPropertyMetadata 时的 PropertyChangedCallback
- 一个值转换器
- 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
)。