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