我开发LOB应用程序,在那里我将需要多个对话窗口(并在一个窗口中显示的一切是不是一种选择/是没有意义的)。
我想对我的窗口的用户控件,将定义一些造型等,将有多个插槽,其中的内容可以被插入-例如,一个模式对话框的模板需要有对内容的插槽,以及按钮(使得用户然后可以提供按钮的具有结合的个ICommand内容和集)。
我想有这样的事情(但不工作):
用户控件XAML:
<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一个窗口喜欢这里 ,但话又说回来:窗口只有一个内容时段,因此它的模板就可以只有一个主持人,但我需要两个(如果在这种情况下,将PO也许可能去一个,还有其他使用情况下,一些内容槽会来的手,只是觉得有关的文章模板 - 控件的用户将提供一个标题,(结构)的内容,作者姓名,图像...)。
谢谢!
PS:如果我想只是通过侧键身边,我怎么可以把多个控件(按钮)的StackPanel? 列表框的ItemsSource有,但StackPanel中已经没有了,它的Children属性是只读的 - 所以,这并不工作(在用户控件内):
<StackPanel
Orientation="Horizontal"
Children="{Binding Buttons}"/>
编辑:我不想使用绑定,因为我想分配一个DataContext(视图模型)的整个窗口(等于查看),然后结合到它从插入控制按钮“插槽”命令 - 因此,任何使用层次结构中的结合将打破查看DC的继承。
至于从HeaderedContentControl继承的想法 - 是的,在这种情况下,它会工作,但如果我想哪三个部分替代的? 如何让我的自己的“HeaderedAndFooteredContentControl”(或者,我将如何实现HeaderedContentControl如果我没有一个)?
EDIT2:OK,所以我的两个解决方案doen't工作-这就是为什么:将ContentPresenter得到它的从DataContext的内容,但我需要包含的元素链接到原始窗口(用户控件的逻辑树父)的DataContext绑定-因为这样一来,当我嵌入势必视图模型的财产文本框,它不绑定,作为继承链已经控制内部碎了 !
看来,我需要拯救父母的DataContext,并将其恢复到所有控制的容器的孩子,但我没有得到的datacontext了在逻辑树被更改的任何事件。
EDIT3:我有一个解决办法! ,删除了我以前的aswers。 看到我的反应。
OK,我的解决方案是完全不必要的,这里是你永远都需要创建任何用户控制的唯一教程:
- http://www.interact-sw.co.uk/iangblog/2007/02/14/wpfdefaulttemplate
- http://www.codeproject.com/Articles/37326/Lookless-Controls-Themes.aspx
- http://www.codeproject.com/Articles/35444/Defining-the-Default-Style-for-a-Lookless-Control.aspx
- http://blog.pixelingene.com/?p=58
简而言之:
子类一些合适的类(或的UIElement如果没有适合你) - 该文件只是普通的*的.cs,因为我们只定义行为,控制的不是长相。
public class EnhancedItemsControl : ItemsControl
添加依赖属性为您的“槽”(普通财产是不够好,因为它具有约束力的只有有限的支持)。 酷招:在VS,写propdp
然后按Tab键展开片段:):
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*/);
添加属性对于一个设计师(因为您要建立所谓的无外观控制),这样,我们说,我们需要有一个在我们的模板被称为PART_AlternativeContentPresenter ContentPresenter
[TemplatePart(Name = "PART_AlternativeContentPresenter", Type = typeof(ContentPresenter))]
public class EnhancedItemsControl : ItemsControl
提供一个静态构造函数,将告诉WPF的造型系统,我们的类(没有它,款式/靶向我们的新类型的模板将不适用):
static EnhancedItemsControl()
{
DefaultStyleKeyProperty.OverrideMetadata(
typeof(EnhancedItemsControl),
new FrameworkPropertyMetadata(typeof(EnhancedItemsControl)));
}
如果你想从模板做的ContentPresenter东西,你通过重写OnApplyTemplate方法做到这一点:
//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...
}
}
提供一个默认的模板:总是ProjectFolder /主题/ Generic.xaml(我有我所有的自定义可通用的WPF控件独立的项目,然后从其他解决方案引用)。 这是唯一的地方,系统会寻找你的控制模板,所以把所有控件的默认模板项目中的位置:在这个片段中,我定义它显示我们的价值新ContentPresenter AlternativeContent
附加属性。 注意语法-我可以用任何Content="{Binding AlternativeContent, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type WPFControls:EnhancedItemsControl}}}"
Content="{TemplateBinding AlternativeContent}"
但前者会工作,如果你定义一个模板模板内侧(必要的造型例如ItemPresenters)。
<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>
瞧,你只是做你的第一个无外观的用户控件(添加更多contentpresenters和依赖属性更“内容槽”)。
直到胜利永远!
我来与工作液(在互联网上第一,在我看来:))
最棘手的DialogControl.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));
}
而DialogControl.xaml(无变化):
<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>
如果您使用的是用户控件
我猜你真正想要的:
<ContentPresenter Content="{Binding Buttons}"/>
这是假定传递给控制DataContext的有一个按钮属性。
并用的ControlTemplate
另一种选择将是一个控件模板,然后你可以使用:
<ContentPresenter ContentSource="Header"/>
你将需要模板化实际上有一个“头”来做到这一点(通常是HeaderedContentControl)的控制。
文章来源: WPF: template or UserControl with 2 (or more!) ContentPresenters to present content in 'slots'