要求
让我们从我想要实现的目标开始。我希望有一个带有2列的网格和一个网格分割器(还有一点,但是让它保持简单)。我希望能够在很多不同的地方使用这个网格,所以每次我想创建一个包含两个ContentPresenters
的自定义控件时,不要创建它。
最终目标实际上是能够像这样编写XAML:
<MyControls:MyGrid>
<MyControls:MyGrid.Left>
<Label x:Name="MyLabel">Something unimportant</Label>
</MyControls:MyGrid.Left>
<MyControls:MyGrid.Right>
<Label>Whatever</Label>
</MyControls:MyGrid.Right>
</MyControls:MyGrid>
重要提示:请注意,我要将Name
应用于Label
元素。
尝试1
我做了很多搜索解决方案,我找到的最好的方法是创建一个UserControl
以及定义我的网格的XAML文件。这个XAML文件包含2个ContentPresenter
元素,并且凭借绑定的魔力,我能够获得一些非常棒的工作。但是,该方法的问题是无法Name
嵌套控件,这会导致以下构建错误:
无法在元素'MyGrid'上设置名称属性值'MyName'。 'MyGrid' 属于'MyControls'元素的范围,已经有了一个名字 在另一个范围内定义时注册。
手头有这个错误,我回到谷歌博士......
尝试2(当前)
经过更多的搜索后,我在SO上发现了一些信息,表明问题是由于有一个关联的XAML文件和MyGrid
类,并且问题应该通过删除XAML并创建所有的问题来解决。通过OnInitialized
方法中的代码进行控制。
所以我走了那条路,把它全部编码和编译。好消息是,我现在可以在嵌套的Name
控件中添加Label
,坏消息是无渲染!不在设计模式下,而不是在运行应用程序时。也没有错误。
所以,我的问题是:我错过了什么?我做错了什么?
我也愿意接受其他方式来满足我的要求。
当前代码
public class MyGrid : UserControl
{
public static readonly DependencyProperty LeftProperty = DependencyProperty.Register("Left", typeof(object), typeof(MyGrid), new PropertyMetadata(null));
public object Left
{
get { return (object)GetValue(LeftProperty); }
set { SetValue(LeftProperty, value); }
}
public static readonly DependencyProperty RightProperty = DependencyProperty.Register("Right", typeof(object), typeof(MyGrid), new PropertyMetadata(null));
public object Right
{
get { return (object)GetValue(RightProperty); }
set { SetValue(RightProperty, value); }
}
Grid MainGrid;
static MyGrid()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyGrid), new FrameworkPropertyMetadata(typeof(MyGrid)));
}
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
//Create control elements
MainGrid = new Grid();
//add column definitions
ColumnDefinition leftColumn = new ColumnDefinition()
{
Name = "LeftColumn",
Width = new GridLength(300)
};
MainGrid.ColumnDefinitions.Add(leftColumn);
MainGrid.ColumnDefinitions.Add(new ColumnDefinition()
{
Width = GridLength.Auto
});
//add grids and splitter
Grid leftGrid = new Grid();
Grid.SetColumn(leftGrid, 0);
MainGrid.Children.Add(leftGrid);
GridSplitter splitter = new GridSplitter()
{
Name = "Splitter",
Width = 5,
BorderBrush = new SolidColorBrush(Color.FromArgb(255, 170, 170, 170)),
BorderThickness = new Thickness(1, 0, 1, 0)
};
MainGrid.Children.Add(splitter);
Grid rightGrid = new Grid();
Grid.SetColumn(rightGrid, 1);
MainGrid.Children.Add(rightGrid);
//add content presenters
ContentPresenter leftContent = new ContentPresenter();
leftContent.SetBinding(ContentPresenter.ContentProperty, new Binding("Left") { Source = this });
leftGrid.Children.Add(leftContent);
ContentPresenter rightContent = new ContentPresenter();
rightContent.SetBinding(ContentPresenter.ContentProperty, new Binding("Right") { Source = this });
rightGrid.Children.Add(rightContent);
//Set this content of this user control
this.Content = MainGrid;
}
}
答案 0 :(得分:3)
通过评论进行一些讨论后,很快就发现我的尝试解决方案都不是正确的方法。所以我开始了第三次冒险,希望这个是最终解决方案......而且似乎就是这样!
免责声明:我还没有足够的WPF经验来自信地说我的解决方案是最好的和/或推荐的方法,只是它绝对有效。
首先创建一个新的自定义控件:"Add" > "New Item" > "Custom Control (WPF)"
。这将创建一个继承自Control
。
在这里,我们将我们的依赖属性绑定到out out content presenters:
public class MyGrid : Control
{
public static readonly DependencyProperty LeftProperty = DependencyProperty.Register("Left", typeof(object), typeof(MyGrid), new PropertyMetadata(null));
public object Left
{
get { return (object)GetValue(LeftProperty); }
set { SetValue(LeftProperty, value); }
}
public static readonly DependencyProperty RightProperty = DependencyProperty.Register("Right", typeof(object), typeof(MyGrid), new PropertyMetadata(null));
public object Right
{
get { return (object)GetValue(RightProperty); }
set { SetValue(RightProperty, value); }
}
static MyGrid()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyGrid), new FrameworkPropertyMetadata(typeof(MyGrid)));
}
}
当您在Visual Studio中添加此类文件时,它将自动在项目中创建一个新的“Generic.xaml”文件,其中包含此控件的样式以及该样式中的控件模板 - 这是我们定义的控制元素......
<Style TargetType="{x:Type MyControls:MyGrid}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type MyControls:MyGrid}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="500" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<ContentPresenter x:Name="LeftContent" />
</Grid>
<GridSplitter Width="5" BorderBrush="#FFAAAAAA" BorderThickness="1,0,1,0">
</GridSplitter>
<Grid Grid.Column="1">
<ContentPresenter x:Name="RightContent" />
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
最后一步是连接2个内容演示者的绑定,然后返回到类文件。
将以下覆盖方法添加到MyGrid
类:
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
//Apply bindings and events
ContentPresenter leftContent = GetTemplateChild("LeftContent") as ContentPresenter;
leftContent.SetBinding(ContentPresenter.ContentProperty, new Binding("Left") { Source = this });
ContentPresenter rightContent = GetTemplateChild("RightContent") as ContentPresenter;
rightContent.SetBinding(ContentPresenter.ContentProperty, new Binding("Right") { Source = this });
}
就是这样!该控件现在可以在其他XAML代码中使用,如下所示:
<MyControls:MyGrid>
<MyControls:MyGrid.Left>
<Label x:Name="MyLabel">Something unimportant</Label>
</MyControls:MyGrid.Left>
<MyControls:MyGrid.Right>
<Label>Whatever</Label>
</MyControls:MyGrid.Right>
</MyControls:MyGrid>
感谢到@NovitchiS的输入,您的建议对于实现这种方法至关重要