【问题标题】:WPF - Swapping ContentControl (relinking elements???)WPF - 交换 ContentControl(重新链接元素???)
【发布时间】:2009-07-08 17:42:39
【问题描述】:

我正在开发一个自定义面板控件,我试图让它做的一件事是在运行时交换它的内容。此控件有两种状态:最大化和正常。当用户单击控件上的按钮时,状态切换。此控件有两个属性:MaximizedContent 和 MinimizedContent。单击交换状态的按钮时,控件的 Content 属性需要在 MaximizedContent 和 MinimizedContent 之间进行交换。当 MaximizedContent 或 MinimizedContent 内部存在绑定时,就会出现问题。似乎不是“树”的一部分,因此绑定不起作用......至少这是我的理论。所以我的问题是如何让它们成为树的一部分?

这是一个简化的例子:

MainWindow.xaml

<Window x:Class="SwappingContentTest.MainWindow"
        Loaded="Window_Loaded"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:local="clr-namespace:SwappingContentTest"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <StackPanel  HorizontalAlignment="Left">
        <Button x:Name="swapContentButton"
                Click="swapContentButton_Click"
                Content="Swap Content" />

        <local:SwappableContentControl x:Name="swappableControl">
            <local:SwappableContentControl.MaximizedContent>
                <StackPanel>
                    <CheckBox x:Name="maximizedCheckBox"
                              Content="Maximized CheckBox" />
                    <Button x:Name="maximizedButton"
                            Content="Maximized Button"
                            IsEnabled="{Binding ElementName=maximizedCheckBox, Path=IsChecked}" />
                </StackPanel>
            </local:SwappableContentControl.MaximizedContent>

            <local:SwappableContentControl.MinimizedContent>
                <StackPanel>
                    <CheckBox x:Name="minimizedCheckBox"
                              Content="Minimized CheckBox" />
                    <Button x:Name="minimizedButton"
                            Content="Minimized Button"
                            IsEnabled="{Binding ElementName=minimizedCheckBox, Path=IsChecked}" />
                </StackPanel>
            </local:SwappableContentControl.MinimizedContent>
        </local:SwappableContentControl>

        <CheckBox x:Name="standardCheckBox"
                  Content="Standard CheckBox"
                  Margin="0,20,0,0"/>
        <Button x:Name="standardButton"
                Content="StandardButton"
                IsEnabled="{Binding ElementName=standardCheckBox, Path=IsChecked}" />
    </StackPanel>
</Window>

MainWindow.cs

namespace SwappingContentTest
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            swappableControl.SwapContent();
        }

        private void swapContentButton_Click(object sender, RoutedEventArgs e)
        {
            swappableControl.SwapContent();
        }
    }
}

SwappableContentControl.cs

namespace SwappingContentTest
{
    public class SwappableContentControl : ContentControl
    {
        public static readonly DependencyProperty MaximizedContentProperty = DependencyProperty.Register("MaximizedContent", typeof(object), typeof(SwappableContentControl));

        public static readonly DependencyProperty MinimizedContentProperty = DependencyProperty.Register("MinimizedContent", typeof(object), typeof(SwappableContentControl));

        public static readonly DependencyProperty StateProperty = DependencyProperty.Register("State", typeof(SwappableContentControlState), typeof(SwappableContentControl),
            new PropertyMetadata(new PropertyChangedCallback(StatePropertyCallback)));

        public static void StatePropertyCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            SwappableContentControl control = (SwappableContentControl)d;
            if ((SwappableContentControlState)e.NewValue == SwappableContentControlState.Maximized)
            {
                control.Content = control.MaximizedContent;
            }
            else
            {
                control.Content = control.MinimizedContent;
            }
        }

        public object MaximizedContent
        {
            get { return GetValue(MaximizedContentProperty); }
            set { SetValue(MaximizedContentProperty, value); }
        }


        public object MinimizedContent
        {
            get { return GetValue(MinimizedContentProperty); }
            set { SetValue(MinimizedContentProperty, value); }
        }


        public SwappableContentControlState State
        {
            get { return (SwappableContentControlState)GetValue(StateProperty); }
            set { SetValue(StateProperty, value); }
        }

        public void SwapContent()
        {
            if (State == SwappableContentControlState.Maximized)
            {
                State = SwappableContentControlState.Normal;
            }
            else
            {
                State = SwappableContentControlState.Maximized;
            }
        }
    }
}

这是项目的链接: http://www.freewebs.com/thrash505/SwappingContentTest.zip

【问题讨论】:

  • 请多解释一下是什么问题。并尝试将所有代码简化为更易于管理的代码,例如 App.cs 或所有 DependecyProperty 代码之类的文件可以删除,让您的问题更容易理解
  • 我减少了一些代码,希望对您有所帮助。最好下载该项目并亲自查看。我有一个带有两个额外内容属性的 ContentControl;一种用于最大化,一种用于最小化。我在运行时将这些内容交换到实际的 Content 属性中。但是,由于加载控件时没有实际树的一部分,所以一旦我在运行时交换它们,绑定就不起作用。当我交换时,我需要弄清楚如何使它们成为树的一部分......我认为这是问题所在,但我可能错了。

标签: c# wpf binding controls


【解决方案1】:

我建议不要交换内容本身,而是在您的控件中放置两个 ContentControl 实例并更改可见性。除了整体更干净之外,这将具有仅更新控件布局而不强制重建树的性能优势。此外,这意味着两个 ContentControl 始终保留在逻辑树中,使它们更容易在您的控件实现中引用并保持绑定正确更新。此外,您还可以获得它们可以单独模板化的好处,从而为漂亮的视觉状态更改打开了大门。

【讨论】:

  • 我不久前考虑过这样做,但由于某种原因,我认为它会更慢......主要是因为屏幕上两个内容的所有控件都同时出现......但是现在我再想一想,渲染引擎、输入引擎和诸如此类的东西可能对不可见的控件没有任何作用......我同意你的建议,我会尝试一下,谢谢。
  • 只要您使用 'Collapsed' 隐藏而不是 'Hidden' 你的表现应该没问题,因为这只会更新布局而不是重建视觉和逻辑树。
【解决方案2】:

在初始示例中,交换内容后将生成以下绑定错误:

System.Windows.Data 错误:4:找不到绑定源 参考 'ElementName=maximizedCheckBox'。 绑定表达式:路径=IsChecked;数据项=空;目标元素是 '按钮'(名称='最大化按钮');目标属性是“IsEnabled” (类型“布尔”)

FrameworkElement 提供了两个方法:AddLogicalChild 和 RemoveLogicalChild

任何“内容”依赖属性都应提供值更改回调。在此回调处理程序中,为旧内容调用 RemoveLogicalChild 并为新内容调用 AddLogicalChild。这使逻辑树保持同步并支持 DataContext 模型。

查看最初的示例,MinimizedContent 的新实现如下所示:

// MinimizedContent
public static readonly DependencyProperty MinimizedContentProperty
    = DependencyProperty.Register("MinimizedContent", typeof(object), typeof(SwappableContentControl),
    new PropertyMetadata(new PropertyChangedCallback(OnMinimizedContentChanged)));

public object MinimizedContent
{
    get { return GetValue(MinimizedContentProperty); }
    set { SetValue(MinimizedContentProperty, value); }
}

private static void OnMinimizedContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    SwappableContentControl swappableContentControl = d as SwappableContentControl;
    if (swappableContentControl == null)
        return;

    swappableContentControl.RemoveLogicalChild(e.OldValue);
    swappableContentControl.AddLogicalChild(e.NewValue);
}

调用 AddLogicalChild 和 RemoveLogicalChild 可解决问题,并且绑定按预期工作。

【讨论】:

    猜你喜欢
    • 2010-12-02
    • 1970-01-01
    • 2022-01-07
    • 1970-01-01
    • 2013-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多