【问题标题】:how to write an <If> user control for WPF XAML [duplicate]如何为 WPF XAML 编写 <If> 用户控件 [重复]
【发布时间】:2021-07-23 13:11:29
【问题描述】:

我在编写用户控件方面取得了一些进展,该控件不仅隐藏内容,而且在不满足某些条件时将其完全删除。

几个示例用例:

<mynamespace:If Condition="{Binding PerformingWork}">
    <LoadingThrobber />
</mynamespace:If>
<mynamespace:If Condition="{Binding HasResults}">
    <ItemsControl ...>
        ...
    </ItemsControl>
</mynamespace:If>

<mynamespace:If Condition="{Binding UserHasMorePermissions}">
    ...
</mynamespace:If>

等等

我的解决方案似乎可以工作,但有一个严重的缺点:If“块”的内容仍然加载一次,然后被删除(通过在某些子控件的构造函数中放置 MessageBox.Show(...) 很容易证明)。我想阻止内容运行,以防它试图做一些繁重的工作,或者对尚不存在的数据进行操作等。

这是我到目前为止的想法:

    public partial class If : UserControl
    {
        private object ConditionalContent { get; set; }

        public If()
        {
            InitializeComponent();

            Loaded += OnLoad;
        }

        private void OnLoad(object sender, RoutedEventArgs e)
        {
            ConditionalContent = Content;
            Loaded -= OnLoad;
            OnConditionChanged();
        }

        private void OnConditionChanged()
        {
            Content = Condition ? ConditionalContent : null;
        }

        // === component properties

        // omg, WPF
        public bool Condition
        {
            get { return (bool)GetValue(ConditionProperty); }
            set { SetValue(ConditionProperty, value); }
        }

        public static readonly DependencyProperty ConditionProperty = DependencyProperty.Register("Condition", typeof(bool), typeof(If), new PropertyMetadata(OnConditionChangedCallback));
        
        private static void OnConditionChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            if (sender is If s)
            {
                s.OnConditionChanged();
            }
        }
    }

XAML 基本上是空的;我将它包括在内,以防这里有重要的事情:

<UserControl x:Class="XamlExtensions.If"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:XamlExtensions"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
</UserControl>

当然,如果有其他方法可以实现相同的目标,那就太好了。

【问题讨论】:

  • 标记扩展似乎很合适。或者您可以将控件子类化并操作其控件模板,当条件评估为 false 时将其设为 null
  • 显示/隐藏某些内容的更通用方法是使用data trigger 操作可见性或控制模板。您也可以使用converter 将条件绑定到Visibility。有很多方法。
  • 这不是一个好主意。您将使用冗余的用户控件使您的可视化树膨胀。这会降低应用程序的性能,尤其是在渲染方面。您应该使用triggers、样式和模板。绑定转换器也是一个选项(如前所述):How to: Convert Bound Data。您只需使用触发器切换UIElement.Visibility
  • 你能不能这样设计屏幕,在那里你可以利用内容控制或数据模板?

标签: c# wpf xaml


【解决方案1】:

我会使用 Custom Control 而不是 User Control,并将默认子控件设为自定义 DependencyProperty 并手动添加到控件的内容中

[ContentProperty("ConditionalContent")]
public partial class If : ContentControl
{
    public If()
    {
        Loaded += OnLoad;
    }

    private void OnLoad(object sender, RoutedEventArgs e)
    {
        update();
    }

    private void update()
    {
        Content = Condition ? ConditionalContent : null;
    }

    // === component properties

    // omg, WPF
    public bool Condition
    {
        get { return (bool)GetValue(ConditionProperty); }
        set { SetValue(ConditionProperty, value); }
    }

    public static readonly DependencyProperty ConditionProperty = DependencyProperty.Register("Condition", typeof(bool), typeof(If), new PropertyMetadata(OnConditionChangedCallback));

    private static void OnConditionChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        if (sender is If s)
        {
            s.update();
        }
    }



    public object ConditionalContent
    {
        get { return (object)GetValue(ConditionalContentProperty); }
        set { SetValue(ConditionalContentProperty, value); }
    }

    public static readonly DependencyProperty ConditionalContentProperty =
        DependencyProperty.Register("ConditionalContent", typeof(object), typeof(If), new PropertyMetadata(null,OnConditionChangedCallback));


}

它是这样工作的

<local:If Condition="{Binding}">
    sdgdfhsh <!-- or anything else! -->
</local:If>

【讨论】:

  • 这是有希望的,因为子内容没有被渲染!呜呼!但是 Condition 绑定似乎很混乱;我在 Condition 属性中绑定到的任何内容都会得到 property not found on object of type If
  • 我想知道你是否真的想使用Condition="{Binding}",我能够让它工作,有点?这在我看来是错误的,并且 VS 会引发 XAML 绑定失败,但该组件确实有效! &lt;local:If DataContext="{Binding SomeBoolPropertyOfDataContext}" Condition="{Binding}"&gt;...&lt;/local:If&gt; 有没有办法在 Condition 中使用“SomeBoolPropertyOfDataContext”?
  • 以上cmet可能只是我自己缺乏WPF知识;我会四处搜索并弄清楚。你的答案肯定有效,或者至少有 99%。一旦我弄清楚它应该是什么,我会建议对您的 XAML 代码进行编辑。感谢您的快速回答!
  • 我并不是说你必须使用Binding,我是在演示控件将动态更新。
猜你喜欢
  • 1970-01-01
  • 2012-04-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-01
  • 1970-01-01
  • 2011-08-16
相关资源
最近更新 更多