WPF:template或UserControl,带有2个(或更多!)ContentPresenter以';插槽';
我正在开发LOB应用程序,我需要多个对话框窗口(在一个窗口中显示所有内容不是一个选项/没有意义) 我希望为我的窗口提供一个用户控件,该控件将定义一些样式等,并且将有几个可以插入内容的插槽-例如,一个模态对话框窗口的模板将有一个用于内容和按钮的插槽(这样用户就可以提供一个内容和一组带有绑定ICommand的按钮) 我想要这样的东西(但这不起作用): UserControl xaml:WPF:template或UserControl,带有2个(或更多!)ContentPresenter以';插槽';,wpf,user-controls,contentpresenter,Wpf,User Controls,Contentpresenter,我正在开发LOB应用程序,我需要多个对话框窗口(在一个窗口中显示所有内容不是一个选项/没有意义) 我希望为我的窗口提供一个用户控件,该控件将定义一些样式等,并且将有几个可以插入内容的插槽-例如,一个模态对话框窗口的模板将有一个用于内容和按钮的插槽(这样用户就可以提供一个内容和一组带有绑定ICommand的按钮) 我想要这样的东西(但这不起作用): UserControl xaml: <UserControl x:Class="TkMVVMContainersSample.Services.
<UserControl x:Class="TkMVVMContainersSample.Services.Common.GUI.DialogControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"
>
<DockPanel>
<DockPanel
LastChildFill="False"
HorizontalAlignment="Stretch"
DockPanel.Dock="Bottom">
<ContentPresenter ContentSource="{Binding Buttons}"/>
</DockPanel>
<Border
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Padding="8"
>
<ContentPresenter ContentSource="{Binding Controls}"/>
</Border>
</DockPanel>
</UserControl>
这样的事情可能吗?我应该如何告诉VS我的控件公开了两个内容占位符,以便我可以这样使用它
<Window ... DataContext="MyViewModel">
<gui:DialogControl>
<gui:DialogControl.Controls>
<!-- My dialog content - grid with textboxes etc...
inherits the Window's DC - DialogControl just passes it through -->
</gui:DialogControl.Controls>
<gui:DialogControl.Buttons>
<!-- My dialog's buttons with wiring, like
<Button Command="{Binding HelpCommand}">Help</Button>
<Button Command="{Binding CancelCommand}">Cancel</Button>
<Button Command="{Binding OKCommand}">OK</Button>
- they inherit DC from the Window, so the OKCommand binds to MyViewModel.OKCommand
-->
</gui:DialogControl.Buttons>
</gui:DialogControl>
</Window>
或者我可以为窗口使用ControlTemplate,但话说回来:窗口只有一个内容槽,因此它的模板只能有一个演示者,但我需要两个(如果在这种情况下,可能会使用一个,那么还有一些其他的用例,其中会有几个内容槽,只需考虑一个供article-control用户使用的模板,它将提供标题、(结构化)内容、作者姓名、图像……)
谢谢大家!
PS:如果我只想让按钮并排放置,如何将多个控件(按钮)放置到StackPanel?ListBox有ItemsSource,但StackPanel没有,并且它的Children属性是只读的-因此这不起作用(在usercontrol内):
编辑:我不想使用绑定,因为我想将DataContext(ViewModel)分配给整个窗口(等于View),然后从插入控件“插槽”的按钮绑定到它的命令,所以层次结构中任何绑定的使用都会破坏View的DC继承
至于从HeaderedContentControl继承的想法-是的,在这种情况下它会起作用,但是如果我想要三个可替换的部分呢?我如何制作自己的“HeaderedFooterdContentControl”(或者,如果我没有HeaderedContentControl,我将如何实现它)
EDIT2:好的,所以我的两个解决方案不起作用-这就是原因:
ContentPresenter从DataContext获取其内容,但我需要包含元素上的绑定来链接到原始windows(逻辑树中UserControl的父级)DataContext-因为这样,当我将绑定到ViewModel属性的textbox嵌入时,它没有绑定,因为继承链在控件中已断开
似乎我需要保存父控件的DataContext,并将其还原到所有控件容器的子控件,但我没有得到逻辑树中DataContext发生更改的任何事件
EDIT3:我有一个解决方案!,删除了我以前的提问。
查看我的回答。如果您使用的是用户控件 我猜你真的想要:
<ContentPresenter Content="{Binding Buttons}"/>
这假定传递给控件的DataContext具有Buttons属性
并使用控制模板
另一个选项是ControlTemplate,然后您可以使用:
<ContentPresenter ContentSource="Header"/>
您需要为一个实际具有“Header”的控件(通常为HeaderedContentControl)制作模板。Hasta la victoria siempre! 我带来了工作解决方案(首先是在互联网上,在我看来:)) 棘手的对话框control.xaml.cs-请参阅注释:
public partial class DialogControl : UserControl
{
public DialogControl()
{
InitializeComponent();
//The Logical tree detour:
// - we want grandchildren to inherit DC from this (grandchildren.DC = this.DC),
// but the children should have different DC (children.DC = this),
// so that children can bind on this.Properties, but grandchildren bind on this.DataContext
this.InnerWrapper.DataContext = this;
this.DataContextChanged += DialogControl_DataContextChanged;
// need to reinitialize, because otherwise we will get static collection with all buttons from all calls
this.Buttons = new ObservableCollection<FrameworkElement>();
}
void DialogControl_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
/* //Heading is ours, we want it to inherit this, so no detour
if ((this.GetValue(HeadingProperty)) != null)
this.HeadingContainer.DataContext = e.NewValue;
*/
//pass it on to children of containers: detours
if ((this.GetValue(ControlProperty)) != null)
((FrameworkElement)this.GetValue(ControlProperty)).DataContext = e.NewValue;
if ((this.GetValue(ButtonProperty)) != null)
{
foreach (var control in ((ObservableCollection<FrameworkElement>) this.GetValue(ButtonProperty)))
{
control.DataContext = e.NewValue;
}
}
}
public FrameworkElement Control
{
get { return (FrameworkElement)this.GetValue(ControlProperty); }
set { this.SetValue(ControlProperty, value); }
}
public ObservableCollection<FrameworkElement> Buttons
{
get { return (ObservableCollection<FrameworkElement>)this.GetValue(ButtonProperty); }
set { this.SetValue(ButtonProperty, value); }
}
public string Heading
{
get { return (string)this.GetValue(HeadingProperty); }
set { this.SetValue(HeadingProperty, value); }
}
public static readonly DependencyProperty ControlProperty =
DependencyProperty.Register("Control", typeof(FrameworkElement), typeof(DialogControl));
public static readonly DependencyProperty ButtonProperty =
DependencyProperty.Register(
"Buttons",
typeof(ObservableCollection<FrameworkElement>),
typeof(DialogControl),
//we need to initialize this for the designer to work correctly!
new PropertyMetadata(new ObservableCollection<FrameworkElement>()));
public static readonly DependencyProperty HeadingProperty =
DependencyProperty.Register("Heading", typeof(string), typeof(DialogControl));
}
public分部类DialogControl:UserControl
{
公共对话控制()
{
初始化组件();
//逻辑树迂回:
//-我们希望孙子继承此DC(孙子.DC=此.DC),
//但是孩子们应该有不同的DC(children.DC=this),
//这样子代可以绑定到这个.Properties,而孙子代可以绑定到这个.DataContext
this.InnerWrapper.DataContext=此;
this.DataContextChanged+=DialogControl\u DataContextChanged;
//需要重新初始化,否则我们将从所有调用中获取带有所有按钮的静态集合
this.Buttons=新的ObservableCollection();
}
无效对话框Control\u DataContextChanged(对象发送方,DependencyPropertyChangedEventArgs e)
{
/*//标题是我们的,我们希望它继承这个,所以不要绕道而行
如果((this.GetValue(HeadingProperty))!=null)
this.HeadingContainer.DataContext=e.NewValue;
*/
//将它传递给容器的子对象:迂回
如果((this.GetValue(ControlProperty))!=null)
((FrameworkElement)this.GetValue(ControlProperty)).DataContext=e.NewValue;
if((this.GetValue(ButtonProperty))!=null)
{
foreach(变量控制在((ObservableCollection)this.GetValue(ButtonProperty))中)
{
control.DataContext=e.NewValue;
}
}
}
公共框架元素控制
{
获取{return(FrameworkElement)this.GetValue(ControlProperty);}
set{this.SetValue(ControlProperty,value);}
}
公共可观察收集按钮
{
获取{return(observateCollection)this.GetValue(ButtonProperty);}
set{this.SetValue(ButtonProperty,value);}
}
公共字符串标题
{
获取{return(string)this.GetValue(HeadingProperty);}
set{this.SetValue(HeadingProperty,value);}
}
公共静态只读从属属性ControlProperty=
DependencyProperty.Register(“控件”、typeof(FrameworkElement)、typeof(DialogControl));
公共静态只读从属属性按钮属性=
从属属性。寄存器(
“按钮”,
类型(可观察的)
public partial class DialogControl : UserControl
{
public DialogControl()
{
InitializeComponent();
//The Logical tree detour:
// - we want grandchildren to inherit DC from this (grandchildren.DC = this.DC),
// but the children should have different DC (children.DC = this),
// so that children can bind on this.Properties, but grandchildren bind on this.DataContext
this.InnerWrapper.DataContext = this;
this.DataContextChanged += DialogControl_DataContextChanged;
// need to reinitialize, because otherwise we will get static collection with all buttons from all calls
this.Buttons = new ObservableCollection<FrameworkElement>();
}
void DialogControl_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
/* //Heading is ours, we want it to inherit this, so no detour
if ((this.GetValue(HeadingProperty)) != null)
this.HeadingContainer.DataContext = e.NewValue;
*/
//pass it on to children of containers: detours
if ((this.GetValue(ControlProperty)) != null)
((FrameworkElement)this.GetValue(ControlProperty)).DataContext = e.NewValue;
if ((this.GetValue(ButtonProperty)) != null)
{
foreach (var control in ((ObservableCollection<FrameworkElement>) this.GetValue(ButtonProperty)))
{
control.DataContext = e.NewValue;
}
}
}
public FrameworkElement Control
{
get { return (FrameworkElement)this.GetValue(ControlProperty); }
set { this.SetValue(ControlProperty, value); }
}
public ObservableCollection<FrameworkElement> Buttons
{
get { return (ObservableCollection<FrameworkElement>)this.GetValue(ButtonProperty); }
set { this.SetValue(ButtonProperty, value); }
}
public string Heading
{
get { return (string)this.GetValue(HeadingProperty); }
set { this.SetValue(HeadingProperty, value); }
}
public static readonly DependencyProperty ControlProperty =
DependencyProperty.Register("Control", typeof(FrameworkElement), typeof(DialogControl));
public static readonly DependencyProperty ButtonProperty =
DependencyProperty.Register(
"Buttons",
typeof(ObservableCollection<FrameworkElement>),
typeof(DialogControl),
//we need to initialize this for the designer to work correctly!
new PropertyMetadata(new ObservableCollection<FrameworkElement>()));
public static readonly DependencyProperty HeadingProperty =
DependencyProperty.Register("Heading", typeof(string), typeof(DialogControl));
}
<UserControl x:Class="TkMVVMContainersSample.Views.Common.DialogControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"
>
<DockPanel x:Name="InnerWrapper">
<DockPanel
LastChildFill="False"
HorizontalAlignment="Stretch"
DockPanel.Dock="Bottom">
<ItemsControl
x:Name="ButtonsContainer"
ItemsSource="{Binding Buttons}"
DockPanel.Dock="Right"
>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Padding="8">
<ContentPresenter Content="{TemplateBinding Content}" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Margin="8">
</StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</DockPanel>
<Border
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Padding="8,0,8,8"
>
<StackPanel>
<Label
x:Name="HeadingContainer"
Content="{Binding Heading}"
FontSize="20"
Margin="0,0,0,8" />
<ContentPresenter
x:Name="ControlContainer"
Content="{Binding Control}"
/>
</StackPanel>
</Border>
</DockPanel>
</UserControl>
<Window x:Class="TkMVVMContainersSample.Services.TaskEditDialog.ItemEditView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Common="clr-namespace:TkMVVMContainersSample.Views.Common"
Title="ItemEditView"
>
<Common:DialogControl>
<Common:DialogControl.Heading>
Edit item
</Common:DialogControl.Heading>
<Common:DialogControl.Control>
<!-- Concrete dialog's content goes here -->
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0">Name</Label>
<TextBox Grid.Row="0" Grid.Column="1" MinWidth="160" TabIndex="1" Text="{Binding Name}"></TextBox>
<Label Grid.Row="1" Grid.Column="0">Phone</Label>
<TextBox Grid.Row="1" Grid.Column="1" MinWidth="160" TabIndex="2" Text="{Binding Phone}"></TextBox>
</Grid>
</Common:DialogControl.Control>
<Common:DialogControl.Buttons>
<!-- Concrete dialog's buttons go here -->
<Button Width="80" TabIndex="100" IsDefault="True" Command="{Binding OKCommand}">OK</Button>
<Button Width="80" TabIndex="101" IsCancel="True" Command="{Binding CancelCommand}">Cancel</Button>
</Common:DialogControl.Buttons>
</Common:DialogControl>
</Window>
public class EnhancedItemsControl : ItemsControl
public object AlternativeContent
{
get { return (object)GetValue(AlternativeContentProperty); }
set { SetValue(AlternativeContentProperty, value); }
}
// Using a DependencyProperty as the backing store for AlternativeContent. This enables animation, styling, binding, etc...
public static readonly DependencyProperty AlternativeContentProperty =
DependencyProperty.Register("AlternativeContent" /*name of property*/, typeof(object) /*type of property*/, typeof(EnhancedItemsControl) /*type of 'owner' - our control's class*/, new UIPropertyMetadata(null) /*default value for property*/);
[TemplatePart(Name = "PART_AlternativeContentPresenter", Type = typeof(ContentPresenter))]
public class EnhancedItemsControl : ItemsControl
static EnhancedItemsControl()
{
DefaultStyleKeyProperty.OverrideMetadata(
typeof(EnhancedItemsControl),
new FrameworkPropertyMetadata(typeof(EnhancedItemsControl)));
}
//remember that this may be called multiple times if user switches themes/templates!
public override void OnApplyTemplate()
{
base.OnApplyTemplate(); //always do this
//Obtain the content presenter:
contentPresenter = base.GetTemplateChild("PART_AlternativeContentPresenter") as ContentPresenter;
if (contentPresenter != null)
{
// now we know that we are lucky - designer didn't forget to put a ContentPresenter called PART_AlternativeContentPresenter into the template
// do stuff here...
}
}
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:WPFControls="clr-namespace:MyApp.WPFControls"
>
<!--EnhancedItemsControl-->
<Style TargetType="{x:Type WPFControls:EnhancedItemsControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type WPFControls:EnhancedItemsControl}">
<ContentPresenter
Name="PART_AlternativeContentPresenter"
Content="{Binding AlternativeContent, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type WPFControls:EnhancedItemsControl}}}"
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type WPFControls:EnhancedItemsControl}}}"
/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>