WPF - 交换ContentControl(重新链接元素???)

时间:2009-07-08 17:42:39

标签: c# wpf binding controls

我正在开发一个自定义面板控件,其中一件我要做的事就是在运行时交换它的内容。此控件有两种状态:最大化和正常。当用户单击控件上的按钮时,状态切换。此控件有两个属性: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

2 个答案:

答案 0 :(得分:5)

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

答案 1 :(得分:0)

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

  

System.Windows.Data错误:4:找不到绑定源   引用'ElementName = maximizedCheckBox'。   BindingExpression:路径=器isChecked;的DataItem = NULL;目标元素是   'Button'(Name ='maximizedButton');目标属性是'IsEnabled'   (输入'Boolean')

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

任何“Content”依赖项属性都应提供值更改的回调。在此回调处理程序中,对旧内容调用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可以解决问题,绑定按预期工作。