WPF в пользовательском контроллере устанавливает содержимое элемента controltemplate равным значению свойства зависимости - PullRequest
1 голос
/ 24 марта 2011

Я довольно новичок в 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>

У меня есть код для элемента управления следующим образом:

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, я увижу, что новое содержимое передается, но оно никогда не будет отображаться во время выполнения.

Я не знаю, правильно ли я все это делаю, возможно ли это или нужно просто настроить. Будем весьма благодарны за любые советы по этому поводу и по сценарию такого рода.

Ответы [ 3 ]

2 голосов
/ 24 марта 2011

Хотя это не прямой ответ на вашу проблему, но вы должны поместить содержимое в элемент управления, а не использовать свойство зависимости, гораздо более читабельное. Вместо использования UserControl создайте класс, который расширяет ContentControl:

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 с содержимым NAMED )

1 голос
/ 24 марта 2011

Это общий шаблон в WPF, который используется многими встроенными элементами управления, производными от ContentControl (1 фрагмент дочернего содержимого), HeaderedContentControl (2 фрагмента дочернего содержимого) или ItemsControl (набор из n дочерних элементов).,В общем, свойства Content (вещи, которые будут подставляться в заполнители во время выполнения) должны иметь тип 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));

С этим новым свойством Content вы можете затем установить Binding с помощью ContentPresenter, который просто действует как заполнительдля вашего Контента, который передается. Это еще проще настроить в пользовательских элементах управления, производных от ContentControl, где ContentPresenter может быть автоматически подключен к Контенту внутри шаблона ControlTemplate.объявлять экземпляры 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, вам будет проще получить то, что вам нужно.

0 голосов
/ 13 июля 2015

Для тех, кто сталкивался с подобной проблемой, но не мог найти ответ где-нибудь еще:

Если вы хотите привязать свойство дочернего элемента управления к свойству зависимости в пользовательском элементе управления, а затем привязать свойство к этому свойству зависимости в вашем пользовательском интерфейсе следующим образом:

<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 для ваших составных элементов управления на usercontrol, поэтому эти элементы управления могут привязываться к вашему пользовательскому свойству Dependency Property, в то же время сохраняя DataContext, установленный вашим пользовательским интерфейсом (страница в этом примере).

Теперь ваш пользовательский интерфейс обновляет MyCustomProperty usercontrols, который, в свою очередь, обновляет TextBox в привязанном к нему usercontrol.

Источник: http://blog.jerrynixon.com/2013/07/solved-two-way-binding-inside-user.html

...