用户控件中的 WPF 将控件模板的内容设置为依赖属性的值
Posted
技术标签:
【中文标题】用户控件中的 WPF 将控件模板的内容设置为依赖属性的值【英文标题】:WPF in a usercontrol set a controltemplate's content to the value of a dependencyproperty 【发布时间】:2011-07-22 07:57:23 【问题描述】:我对 WPF 很陌生,(现在使用了 3 周),所以我可能会错过一些愚蠢的东西或不明白我在做什么!
我正在尝试创建一个模式类型的弹出窗口,它可以覆盖整个应用程序或它所在的当前控件。我希望这个控件是半透明的,这样用户仍然可以看到后面的内容但不能使用它。然后,他们将能够在继续之前完成模式窗口中的所有内容。
我不想在不同的地方重复代码,所以我的目标是拥有一个可以在我的 XAML 中使用的通用控件,并且每次只需要添加我需要的内容。 IE。褪色、透明度、额外的背景颜色都在一个地方处理,我只需要为它的实例添加特定的功能。
到目前为止,我已经创建了一个名为 jonblind 的用户控件:
<UserControl x:Class="ShapInteractiveClient.View.SampleTests.jonblind"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid x:Name="blindGrid" Grid.RowSpan="2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Opacity="0.82">
<Grid.Background>
<LinearGradientBrush EndPoint="0,1" StartPoint="0,0" MappingMode="RelativeToBoundingBox">
<GradientStop Color="#FFA8CBFE" Offset="1"/>
<GradientStop Color="Red"/>
<GradientStop Color="#FFE1EDFE" Offset="0.147"/>
</LinearGradientBrush>
</Grid.Background>
<ContentControl x:Name="contentTemplateArea" />
</Grid>
</UserControl>
我有一个控件的代码如下:
public partial class jonblind : UserControl
public jonblind()
InitializeComponent();
SetVisibility(this);
[Category("jonblind")]
public bool IsContentVisible
get return (bool)GetValue(IsContentVisibleProperty);
set SetValue(IsContentVisibleProperty, value);
public static readonly DependencyProperty IsContentVisibleProperty = DependencyProperty.Register("IsContentVisible", typeof(bool), typeof(jonblind),
new FrameworkPropertyMetadata(new PropertyChangedCallback(OnIsOverlayContentVisibleChanged)));
private static void OnIsOverlayContentVisibleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
jonblind blind = d as jonblind;
if(blind != null)
SetVisibility(blind);
private static void SetVisibility(jonblind blind)
blind.blindGrid.Visibility = blind.IsContentVisible ? Visibility.Visible : Visibility.Hidden;
[Category("jonblind")]
public ContentControl ContentAreaControl
get return (ContentControl)GetValue(ContentAreaControlProperty);
set SetValue(ContentAreaControlProperty, value);
public static readonly DependencyProperty ContentAreaControlProperty = DependencyProperty.Register("ContentAreaControl", typeof(ContentControl), typeof(jonblind),
new FrameworkPropertyMetadata(new PropertyChangedCallback(OnContentAreaControlChanged)));
private static void OnContentAreaControlChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
jonblind blind = d as jonblind;
if (blind != null && e.NewValue != null && e.NewValue is ContentControl)
blind.contentTemplateArea = e.NewValue as ContentControl;
我可以将它添加到另一个用户控件,如下所示:
<UserControl.Resources>
<ContentControl x:Key="testcontrol">
<StackPanel>
<TextBox VerticalAlignment="Center" HorizontalAlignment="Center" Text="Loading!!!" />
<Button Content="hide me!" Command="Binding Path=alternateblind" />
</StackPanel>
</ContentControl>
</UserControl.Resources>
<SampleTests:jonblind IsContentVisible="Binding Path=ShowBlind" ContentAreaControl="StaticResource testcontrol" />
如果我在“OnContentAreaControlChanged”上设置断点,我可以看到传入的新内容,但它不会在运行时显示。
我不知道我是否将这一切都弄错了,是否有可能,或者是否只需要调整一下。对此和处理这种情况的任何和所有建议将不胜感激。
【问题讨论】:
【参考方案1】:虽然这不是您问题的直接答案,但您应该将内容放在控件中,而不是使用依赖属性,这样更具可读性。创建一个扩展 ContentControl 的类,而不是使用 UserControl:
public class jonblind : ContentControl
[Category("jonblind")]
public bool IsContentVisible
get return (bool)GetValue(IsContentVisibleProperty);
set SetValue(IsContentVisibleProperty, value);
public static readonly DependencyProperty IsContentVisibleProperty = DependencyProperty.Register("IsContentVisible", typeof(bool), typeof(jonblind),
new FrameworkPropertyMetadata(new PropertyChangedCallback(OnIsOverlayContentVisibleChanged)));
private static void OnIsOverlayContentVisibleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
jonblind blind = d as jonblind;
if(blind != null)
SetVisibility(blind);
private static void SetVisibility(jonblind blind)
blind.blindGrid.Visibility = blind.IsContentVisible ? Visibility.Visible : Visibility.Hidden;
然后使用样式指定内容
<Style TargetType="control:jonblind">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="control:jonblind">
<Grid>
<Grid.Background>
<LinearGradientBrush EndPoint="0,1" StartPoint="0,0" MappingMode="RelativeToBoundingBox">
<GradientStop Color="#FFA8CBFE" Offset="1"/>
<GradientStop Color="Red"/>
<GradientStop Color="#FFE1EDFE" Offset="0.147"/>
</LinearGradientBrush>
</Grid.Background>
<ContentControl Content="TemplateBinding Content"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
最后 - 使用它
<control:jonblind IsContentVisible="Binding Path=ShowBlind">
<StackPanel>
<TextBox VerticalAlignment="Center" HorizontalAlignment="Center" Text="Loading!!!" />
<Button Content="hide me!" Command="Binding Path=alternateblind" />
</StackPanel>
</control:jonblind>
(改编自此线程的示例:How to create a WPF UserControl with NAMED content)
【讨论】:
这两个答案都很有趣,明天都试试。谢谢 如果我能排除这两个答案,我会得到的。谢谢【参考方案2】:这是 WPF 中的一种常见模式,许多从 ContentControl(1 个子内容)、HeaderedContentControl(2 个子内容)或 ItemsControl(n 个子内容的集合)派生的内置控件都使用这种模式.一般来说,内容属性(将在运行时替换为占位符的东西)应该是对象类型。在这种情况下,您也可以摆脱更改处理程序而只依赖数据绑定。
[Category("jonblind")]
public object ContentAreaControl
get return GetValue(ContentAreaControlProperty);
set SetValue(ContentAreaControlProperty, value);
public static readonly DependencyProperty ContentAreaControlProperty =
DependencyProperty.Register("ContentAreaControl", typeof(object), typeof(jonblind),
new FrameworkPropertyMetadata(null));
使用这个新的 Content 属性,您可以使用 ContentPresenter 设置一个绑定,它只是作为您传入的内容的占位符。这在从 ContentControl 派生的自定义控件中设置更容易,其中 ContentPresenter 可以是在 ControlTemplate 中自动连接到 Content。
<UserControl x:Class="ShapInteractiveClient.View.SampleTests.jonblind"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid x:Name="blindGrid" Grid.RowSpan="2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Opacity="0.82">
<Grid.Background>
<LinearGradientBrush EndPoint="0,1" StartPoint="0,0" MappingMode="RelativeToBoundingBox">
<GradientStop Color="#FFA8CBFE" Offset="1"/>
<GradientStop Color="Red"/>
<GradientStop Color="#FFE1EDFE" Offset="0.147"/>
</LinearGradientBrush>
</Grid.Background>
<ContentPresenter Content="Binding Path=ContentAreaContent, RelativeSource=RelativeSource Mode=FindAncestor, AncestorType=x:Type UserControl" />
</Grid>
</UserControl>
一般来说,将 UIElement 实例声明为资源是个坏主意(最好将它们放在模板资源中),但我们可以通过将其视为任何其他控件来轻松解决该问题。其用法更像是 ContentControl(如 Button)的样子:
<SampleTests:jonblind IsContentVisible="Binding Path=ShowBlind">
<SampleTests:jonblind.ContentAreaControl>
<StackPanel>
<TextBox VerticalAlignment="Center" HorizontalAlignment="Center" Text="Loading!!!" />
<Button Content="hide me!" Command="Binding Path=alternateblind" />
</StackPanel>
</SampleTests:jonblind.ContentAreaControl>
</SampleTests:jonblind>
将其作为自定义 ContentControl 而不是 UserControl 可以获得更多优势,但会增加一些复杂性,更好地理解这些概念通常有助于使其正常工作。当您刚开始坚持使用 UserControl 时,可以更轻松地完成您需要完成的工作。
【讨论】:
这两个答案都很有趣,明天都试试。谢谢【参考方案3】:对于遇到与此类似问题但在其他地方找不到答案的任何人:
如果您想将子控件的属性绑定到用户控件中的依赖项属性,然后在您的 UI 中将属性绑定到该依赖项属性,如下所示:
<Page>
<my:usercontrol MyCustomPoperty="Binding MyData"/>
</Page>
您必须执行以下操作(花了我几个小时才弄清楚):
<UserControl x:Class="my.usercontrol">
<TextBox Text="Binding MyCustomProperty">
</UserControl>
在构造函数后面的代码中:
public usercontrol()
InitializeComponent();
(this.Content as FrameworkElement).DataContext = this;
这会将您组合控件的 DataContext 设置为用户控件,因此这些控件可以绑定到您的自定义依赖属性,同时保留您的 UI 设置的 DataContext(本示例中的页面)。
现在您的 UI 会更新用户控件的 MyCustomProperty,这反过来会更新绑定到它的用户控件中的 TextBox。
来源:http://blog.jerrynixon.com/2013/07/solved-two-way-binding-inside-user.html
【讨论】:
以上是关于用户控件中的 WPF 将控件模板的内容设置为依赖属性的值的主要内容,如果未能解决你的问题,请参考以下文章