【问题标题】:WPF Control Template Not Changing Based on Dependency Property Bound ValueWPF 控件模板不根据依赖属性绑定值更改
【发布时间】:2021-08-26 18:06:30
【问题描述】:

我的任务是创建一个新的布局控件,用于我们的 Windows 应用程序范围,应该有两种不同的布局;在特定 Windows 上显示哪一个将基于依赖属性。我从来没有做过类似的事情,但根据我的研究,似乎应该使用Control。为了了解这一点,我尝试创建新的Control,它将根据属性值显示TextBoxLabel。我按类创建:

public class DetailsLayout : Control
{
    public static readonly DependencyProperty OverlayProperty =
            DependencyProperty.Register("Overlay", typeof(bool), typeof(DetailsLayout),
                new FrameworkPropertyMetadata(true));

    public bool Overlay
    {
        get { return (bool)GetValue(OverlayProperty); }
        set
        {
            OnPropertyChanged(nameof(Overlay));
            SetValue(OverlayProperty, value);
        }
    }

    static DetailsLayout()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(DetailsLayout), new FrameworkPropertyMetadata(typeof(DetailsLayout)));
    }

    public DetailsLayout()
    {
        DefaultStyleKey = typeof(DetailsLayout);
    }

MainWindow 我有:

<layout:DetailsLayout Overlay="False">       
</layout:DetailsLayout>

对于测试,如果Overlay = True 我想显示TextBox,如果Overlay = False 它应该显示标签。为了做到这一点,我尝试了几件事。首先我尝试创建两个ControlTemplate

<ControlTemplate x:Key="OverlayTemplate">
    <TextBox Text="This is the default (TRUE) layout (OVERLAY) with a textbox."/>
</ControlTemplate>

<ControlTemplate x:Key="PushTemplate">
    <Label Content="This is the optional (FALSE) layout (PUSH) with a label."/>
</ControlTemplate>

然后通过样式定义使用这些:

<Style TargetType="{x:Type layout:DetailsLayout}">
    <Setter Property="Template">
       <Setter.Value>
            <ControlTemplate TargetType="{x:Type layout:DetailsLayout}">
                <ContentControl Content="{Binding}">
                    <ContentControl.Style>
                        <Style TargetType="ContentControl">
                            <Setter Property="Template" Value="{StaticResource OverlayTemplate}"/>
                            <Style.Triggers>
                                <DataTrigger Binding="{Binding Overlay, UpdateSourceTrigger=PropertyChanged}" Value="False">
                                    <Setter Property="Template" Value="{StaticResource PushTemplate}"/>
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </ContentControl.Style>
                </ContentControl>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

我还尝试使用DataTrigger's 让它工作;一种具有默认值,一种同时指定了两个值:

两个值

<Style TargetType="{x:Type layout:DetailsLayout}">
    <Style.Triggers>
        <DataTrigger Binding="{Binding Overlay}" Value="True">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type layout:DetailsLayout}">
                        <TextBox Text="This is the default (TRUE) layout (OVERLAY) with a textbox."/>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </DataTrigger>
        <DataTrigger Binding="{Binding Overlay}" Value="False">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type layout:DetailsLayout}">
                        <Label Content="This is the optional (FALSE) layout (PUSH) with a label."/>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </DataTrigger>
    </Style.Triggers>
</Style>

默认

<Style TargetType="{x:Type layout:DetailsLayout}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type layout:DetailsLayout}">
                 <TextBox Text="This is the default layout (OVERLAY) with a textbox."/>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
    <Style.Triggers>
        <DataTrigger Binding="{Binding Overlay}" Value="False">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type layout:DetailsLayout}">
                        <Label Content="This is the optional layout (PUSH) with a label."/>
                    </ControlTemplate>
                </Setter.Value>
             </Setter>
        </DataTrigger>
    </Style.Triggers>
</Style>

目标只是在加载控件时选择正确的模板。因此,一个窗口可以使用&lt;layout:DetailsLayout Overlay="True" /&gt; 之类的控件,而其他窗口将使用&lt;layout:DetailsLayout Overlay="False" /&gt;,在第一个窗口中将显示TextBox,在第二个窗口中将显示Label。但是一旦显示,就不需要更改它,因为 Overlay 值仅在 XAML 中指定(没有控件会在运行时更改此值)。

最后,我还尝试使用DataTemplate,而不是ControlTemplate,如下所示:

<DataTemplate x:Key="OverlayTemplate">
    <TextBox Text="This is the default (TRUE) layout (OVERLAY) with a textbox."/>
</ControlTemplate>

<DataTemplate x:Key="PushTemplate">
    <Label Content="This is the optional (FALSE) layout (PUSH) with a label."/>
</ControlTemplate>

但这也不起作用。任何有关如何使简单示例工作的建议/示例将不胜感激,因为除了我已经尝试过的建议之外,我找不到任何其他建议。提前谢谢你。

【问题讨论】:

  • 为依赖属性实现 INotifyPropertyChanged 是没有意义的。他们确实已经提供了自己的通知机制。删除它,然后更新您的问题。
  • 除此之外,Binding="{Binding Overlay}" 需要声明源对象:Binding="{Binding Overlay, RelativeSource={RelativeSource Self}}"
  • @Clemens 我 99.99999% 确信 INotifyPropertyChanged 没有为此做任何事情,但我认真地尝试了一切,所以我想我会尝试一下。但是添加 RelativeSource 效果很好!非常感谢。
  • 我这样做是为了尝试更改模板。在实际使用中,布局会更加复杂,包含标题部分、主体和滑动绘图,将具有ContentPresenter,因此用户将能够添加他们想要的任何控件,因为这只是一个布局控件。它的开发是因为这是我们所有屏幕的布局方式,并且每个人都对其进行不同的编码,所以我想创建一个可以用来代替网格或堆栈面板的控件,并将实现布局和滑动绘制部分(万一这有什么不同)

标签: wpf templates controls datatemplate controltemplate


【解决方案1】:

非常感谢 Clemens 帮助解决了这个问题。解决方案是将DataTrigger 中的绑定表达式从Binding={Binding Overlay} 更改为Binding={Binding Overlay, RelativeSource={RelativeSource Self}}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-08
    • 2018-05-20
    • 1970-01-01
    • 2023-03-26
    相关资源
    最近更新 更多