C# usercontrol中的WPF设置了一个controltemplate';将内容转换为dependencyproperty的值

C# usercontrol中的WPF设置了一个controltemplate';将内容转换为dependencyproperty的值,c#,wpf,templates,user-controls,contentcontrol,C#,Wpf,Templates,User Controls,Contentcontrol,我是WPF的新手(现在已经使用了3周),所以我可能错过了一些愚蠢的事情或者不明白我在做什么 我正在尝试创建一个模式类型的弹出窗口,它将覆盖整个应用程序或它所在的当前控件。我希望这个控件是半透明的,这样用户仍然可以看到后面的内容,但不能使用它。然后,他们将能够在继续之前完成模式窗口中的所有内容 我不想在不同的地方重复代码,所以我的目标是拥有一个可以在XAML中使用的通用控件,并且每次只需添加所需的内容。也就是说,褪色、透明度、背景色额外都在一个地方处理,我只需为该实例添加特定功能 到目前为止,我已

我是WPF的新手(现在已经使用了3周),所以我可能错过了一些愚蠢的事情或者不明白我在做什么

我正在尝试创建一个模式类型的弹出窗口,它将覆盖整个应用程序或它所在的当前控件。我希望这个控件是半透明的,这样用户仍然可以看到后面的内容,但不能使用它。然后,他们将能够在继续之前完成模式窗口中的所有内容

我不想在不同的地方重复代码,所以我的目标是拥有一个可以在XAML中使用的通用控件,并且每次只需添加所需的内容。也就是说,褪色、透明度、背景色额外都在一个地方处理,我只需为该实例添加特定功能

到目前为止,我已经创建了一个名为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>
我可以将其添加到另一个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”上设置一个断点,我可以看到传入的新内容,但它永远不会在运行时显示它


我不知道我在这件事上是否完全错了,这是否可能,或者这是否仅仅需要一点技巧。如果您能就此提出任何建议,并处理此类情况,我们将不胜感激

虽然这不是问题的直接答案,但您应该将内容放在控件中,而不是使用依赖属性,这样可读性更好。创建扩展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>


(示例改编自此线程:)

这是WPF中的一种常见模式,许多内置控件使用该模式,这些控件派生自ContentControl(1个子内容)、HeaderedContentControl(2个子内容)或ItemsControl(n个子内容的集合)。通常,内容属性(在运行时将替换为占位符的内容)应为object类型。在这种情况下,您还可以摆脱变更处理程序,只依赖于数据绑定

[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));
使用这个新的内容属性,您可以使用ContentPresenter设置绑定,它只是作为传入内容的占位符。这在从ContentControl派生的自定义控件中更容易设置,ContentPresenter可以自动连接到ControlTemplate中的内容

<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(如按钮)的外观:

<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时,可以更简单地完成所需操作。

对于遇到类似问题但在其他地方找不到答案的人:

如果要将子控件的属性绑定到用户控件中的依赖项属性,然后将属性绑定到UI中的该依赖项属性,如下所示:

<Page>
    <my:usercontrol MyCustomPoperty="{Binding MyData}"/>
</Page>
这将组合控件的DataContext设置为usercontrol,因此这些控件可以绑定到自定义依赖项属性,同时保留UI(本例中的页面)设置的DataContext

现在,您的UI将更新usercontrols的MyCustomProperty,而MyCustomProperty将更新绑定到它的usercontrols中的文本框


资料来源:

两个答案都很有趣,明天我们将尝试这两个答案。谢谢,如果我能把这两个答案都排除在外,我会的。谢谢这两个答案都很有趣,明天我将尝试这两个答案。谢谢
<UserControl x:Class="my.usercontrol">
    <TextBox Text="{Binding MyCustomProperty}">
</UserControl>
public usercontrol()
{
    InitializeComponent();
    (this.Content as FrameworkElement).DataContext = this;
}