it-swarm-ko.tech

RelativeSource와 WPF는 바인딩을 사용합니까?

WPF 바인딩과 함께 RelativeSource을 사용하고 다른 유스 케이스는 무엇입니까?

547
David Schmitt

객체의 다른 속성에 바인딩하려는 경우 :

{Binding Path=PathToProperty, RelativeSource={RelativeSource Self}}

조상에 대한 소유권을 얻고 싶다면 :

{Binding Path=PathToProperty,
    RelativeSource={RelativeSource AncestorType={x:Type typeOfAncestor}}}

템플릿 기반 부모에 대한 속성을 가져 오려면 (ControlTemplate에서 양방향 바인딩을 할 수 있도록)

{Binding Path=PathToProperty, RelativeSource={RelativeSource TemplatedParent}}

또는 더 짧습니다 (OneWay 바인딩에만 해당).

{TemplateBinding Path=PathToProperty}
729
Abe Heidebrecht
Binding RelativeSource={
    RelativeSource Mode=FindAncestor, AncestorType={x:Type ItemType}
}
...

RelativeSource의 기본 속성은 Mode 속성입니다. 유효한 값의 전체 집합이 여기에 제공됩니다 ( from MSDN )) :

  • PreviousData 표시된 데이터 항목 목록에서 이전 데이터 항목 (데이터 항목이 포함 된 컨트롤이 아님)을 바인딩 할 수 있습니다.

  • TemplatedParent (데이터 바인딩 요소가있는) 템플릿이 적용되는 요소를 나타냅니다. 이는 TemplateBindingExtension을 설정하는 것과 유사하며 Binding이 템플릿 내에있는 경우에만 적용 할 수 있습니다.

  • Self 바인딩을 설정중인 요소를 참조하고 해당 요소의 한 속성을 동일한 요소의 다른 속성에 바인딩 할 수 있습니다.

  • FindAncestor 데이터 바인딩 요소의 부모 체인에있는 조상을 참조합니다. 이 메소드를 사용하여 특정 유형 또는 그 서브 클래스의 조상 (ancestor)에 바인드 할 수 있습니다. AncestorType 및/또는 AncestorLevel을 지정하려는 경우 사용하는 모드입니다.

125
Drew Noakes

다음은 MVVM 아키텍처와 관련된보다 시각적 인 설명입니다.

enter image description here

120
Jeffrey Knight

이 경우를 상상해보십시오. 높이가 항상 너비와 같기를 바라는 직사각형, 사각형이라고 가정 해 봅시다. 요소 이름을 사용하여이 작업을 수행 할 수 있습니다.

<Rectangle Fill="Red" Name="rectangle" 
                    Height="100" Stroke="Black" 
                    Canvas.Top="100" Canvas.Left="100"
                    Width="{Binding ElementName=rectangle,
                    Path=Height}"/>

그러나 위의 경우에 우리는 바인딩 객체의 이름, 즉 사각형을 나타내야합니다. 우리는 RelativeSource를 사용하여 다르게 같은 목적을 달성 할 수 있습니다.

<Rectangle Fill="Red" Height="100" 
                   Stroke="Black" 
                   Width="{Binding RelativeSource={RelativeSource Self},
                   Path=Height}"/>

이 경우 우리는 바인딩 객체의 이름을 언급 할 의무가 없으며 높이가 변경 될 때마다 너비가 항상 높이와 같습니다.

폭을 높이의 절반으로 지정하려면 Binding 태그 확장에 변환기를 추가하면됩니다. 지금 다른 사례를 상상해 봅시다.

 <TextBlock Width="{Binding RelativeSource={RelativeSource Self},
                   Path=Parent.ActualWidth}"/>

위의 경우는이 요소가 Parent라는 속성을 보유하고 있으므로 주어진 요소의 특정 속성을 직접 부모 요소 중 하나에 묶는 데 사용됩니다. 이것에 의해, FindAncestor의 상대 소스 모드가됩니다.

40

Bechir Bejaoui는 WPF에서 RelativeSources의 사용 사례를 그의 기사는 여기 에서 노출합니다.

RelativeSource는 객체의 속성을 상대 부모의 다른 속성에 바인딩하려고 할 때 특정 객체의 속성을 객체 자체의 다른 속성에 바인딩하려고 할 때 특정 바인딩 사례에 사용되는 태그 확장입니다. 사용자 지정 컨트롤 개발의 경우 XAML 조각에 종속성 속성 값을 바인딩 할 때 마지막으로 일련의 바인딩 된 데이터의 차등을 사용하는 경우에 사용됩니다. 모든 상황은 상대 소스 모드로 표현됩니다. 나는 모든 사건들을 하나씩 공개 할 것이다.

  1. 모드 셀프 :

이 경우를 상상해보십시오. 높이가 항상 너비와 같기를 바라는 직사각형, 사각형이라고 가정 해 봅시다. 요소 이름을 사용하여이 작업을 수행 할 수 있습니다.

<Rectangle Fill="Red" Name="rectangle" 
                Height="100" Stroke="Black" 
                Canvas.Top="100" Canvas.Left="100"
                Width="{Binding ElementName=rectangle,
                Path=Height}"/>

그러나 위의 경우에 우리는 바인딩 객체의 이름, 즉 사각형을 나타내야합니다. 우리는 RelativeSource를 사용하여 다르게 같은 목적을 달성 할 수 있습니다.

<Rectangle Fill="Red" Height="100" 
               Stroke="Black" 
               Width="{Binding RelativeSource={RelativeSource Self},
               Path=Height}"/>

이 경우 우리는 바인딩 객체의 이름을 언급 할 의무가 없으며 높이가 변경 될 때마다 너비가 항상 높이와 같습니다.

폭을 높이의 절반으로 지정하려면 Binding 태그 확장에 변환기를 추가하면됩니다. 지금 다른 사례를 상상해 봅시다.

 <TextBlock Width="{Binding RelativeSource={RelativeSource Self},
               Path=Parent.ActualWidth}"/>

위의 경우는이 요소가 Parent라는 속성을 보유하고 있으므로 주어진 요소의 특정 속성을 직접 부모 요소 중 하나에 묶는 데 사용됩니다. 이것에 의해, FindAncestor의 상대 소스 모드가됩니다.

  1. 모드 FindAncestor

이 경우 특정 요소의 속성은 부모 중 하나 인 Of Corse와 연결됩니다. 위의 경우와의 가장 큰 차이점은 계층 구조에서 조상 유형과 조상 순위를 결정하여 속성을 묶어야한다는 사실입니다. 그런데이 XAML 조각으로 게임을 해보십시오.

<Canvas Name="Parent0">
    <Border Name="Parent1"
             Width="{Binding RelativeSource={RelativeSource Self},
             Path=Parent.ActualWidth}"
             Height="{Binding RelativeSource={RelativeSource Self},
             Path=Parent.ActualHeight}">
        <Canvas Name="Parent2">
            <Border Name="Parent3"
            Width="{Binding RelativeSource={RelativeSource Self},
           Path=Parent.ActualWidth}"
           Height="{Binding RelativeSource={RelativeSource Self},
              Path=Parent.ActualHeight}">
               <Canvas Name="Parent4">
               <TextBlock FontSize="16" 
               Margin="5" Text="Display the name of the ancestor"/>
               <TextBlock FontSize="16" 
                 Margin="50" 
            Text="{Binding RelativeSource={RelativeSource  
                       FindAncestor,
                       AncestorType={x:Type Border}, 
                       AncestorLevel=2},Path=Name}" 
                       Width="200"/>
                </Canvas>
            </Border>
        </Canvas>
     </Border>
   </Canvas>

위의 상황은 계층 적 부모를 나타내는 일련의 테두리와 캔버스 요소 내에 포함 된 두 개의 TextBlock 요소입니다. 두 번째 TextBlock은 상대 소스 레벨에 지정된 부모의 이름을 표시합니다.

따라서 AncestorLevel = 2를 AncestorLevel = 1로 변경하고 어떻게되는지보십시오. 그런 다음 AncestorType = Border에서 AncestorType = Canvas로 조상 유형을 변경하고 어떤 일이 발생하는지 확인하십시오.

표시되는 텍스트는 Ancestor 유형 및 레벨에 따라 변경됩니다. 그런 다음 선조 수준이 조상 유형에 적합하지 않으면 어떻게됩니까? 이것은 좋은 질문입니다, 당신이 그것에 대해 물어볼 것입니다. 응답은 throw되며 예외가 TextBlock 수준에 표시됩니다.

  1. TemplatedParent

이 모드는 주어진 ControlTemplate 속성을 ControlTemplate이 적용된 컨트롤의 속성에 연결합니다. 이 문제를 잘 이해하려면 여기에 예제가 있습니다.

<Window.Resources>
<ControlTemplate x:Key="template">
        <Canvas>
            <Canvas.RenderTransform>
                <RotateTransform Angle="20"/>
                </Canvas.RenderTransform>
            <Ellipse Height="100" Width="150" 
                 Fill="{Binding 
            RelativeSource={RelativeSource TemplatedParent},
            Path=Background}">

              </Ellipse>
            <ContentPresenter Margin="35" 
                  Content="{Binding RelativeSource={RelativeSource  
                  TemplatedParent},Path=Content}"/>
        </Canvas>
    </ControlTemplate>
</Window.Resources>
    <Canvas Name="Parent0">
    <Button   Margin="50" 
              Template="{StaticResource template}" Height="0" 
              Canvas.Left="0" Canvas.Top="0" Width="0">
        <TextBlock FontSize="22">Click me</TextBlock>
    </Button>
 </Canvas>

주어진 컨트롤의 속성을 컨트롤 템플릿에 적용하려면 TemplatedParent 모드를 사용할 수 있습니다. 이 마크 업 확장에는 첫 번째 짧은 손의 일종 인 TemplateBinding도 있지만 첫 번째 런타임 바로 후에 평가되는 TemplatedParent의 대비에서 TemplateBinding은 컴파일 타임에 평가됩니다. 아래 그림에서 알 수 있듯이, 배경과 내용은 버튼 안에서 컨트롤 템플릿에 적용됩니다.

34
Cornel Marian

WPF에서 RelativeSource 바인딩은 세 개의 properties을 설정하여 다음을 설정합니다 .

1. 모드 :이것은 4 개의 값을 가질 수있는 enum입니다.

a. PreviousData (value=0) :property의 이전 값을 바인딩 된 값에 할당합니다

b. TemplatedParent (value=1) :컨트롤의 templates을 정의 할 때 사용되며 control의 값/속성에 바인딩하려고합니다.

예를 들어,ControlTemplate을 정의하십시오.

  <ControlTemplate>
        <CheckBox IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
 </ControlTemplate>

c. Self (value=2) :self 또는 property의 self에서 바인딩하려는 경우.

예 :checkboxCommandParameter을 (를) 설정하면서 Command의 확인 된 상태를 CheckBox (으)로 보냅니다.

<CheckBox ...... CommandParameter="{Binding RelativeSource={RelativeSource Self},Path=IsChecked}" />

d. FindAncestor (value=3) :Visual Tree의 부모 control에서 바인딩하려는 경우.

예 :checkbox, recordsgrid이 선택된 경우 headercheckbox을 바인드

<CheckBox IsChecked="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type iDP:XamDataGrid}}, Path=DataContext.IsHeaderChecked, Mode=TwoWay}" />

2. AncestorType :mode가 FindAncestor 인 경우 조상 유형을 정의하십시오.

RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type iDP:XamDataGrid}}

3. AncestorLevel :모드가 FindAncestor 인 경우 조상 수준 (visual tree에 동일한 유형의 부모가있는 경우)

RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type iDP:XamDataGrid, AncestorLevel=1}}

위는 RelativeSource binding의 모든 사용 사례입니다.

참조 링크가 있습니다 .

25
Kylo Ren

TemplatedParent : 잊지 마세요.

<Binding RelativeSource="{RelativeSource TemplatedParent}"/>

또는

{Binding RelativeSource={RelativeSource TemplatedParent}}
18
Bob King

RelativeSource를 더 쉽게 사용할 수 있도록 WPF의 바인딩 구문을 단순화하는 라이브러리를 만들었습니다. 여기 예시들이 있습니다. 전에:

{Binding Path=PathToProperty, RelativeSource={RelativeSource Self}}
{Binding Path=PathToProperty, RelativeSource={RelativeSource AncestorType={x:Type typeOfAncestor}}}
{Binding Path=PathToProperty, RelativeSource={RelativeSource TemplatedParent}}
{Binding Path=Text, ElementName=MyTextBox}

후:

{BindTo PathToProperty}
{BindTo Ancestor.typeOfAncestor.PathToProperty}
{BindTo Template.PathToProperty}
{BindTo #MyTextBox.Text}

다음은 메소드 바인딩이 단순화되는 방법의 예입니다. 전에:

// C# code
private ICommand _saveCommand;
public ICommand SaveCommand {
 get {
  if (_saveCommand == null) {
   _saveCommand = new RelayCommand(x => this.SaveObject());
  }
  return _saveCommand;
 }
}

private void SaveObject() {
 // do something
}

// XAML
{Binding Path=SaveCommand}

후:

// C# code
private void SaveObject() {
 // do something
}

// XAML
{BindTo SaveObject()}

여기에서 라이브러리를 찾을 수 있습니다. http://www.simplygoodcode.com/2012/08/simpler-wpf-binding.html

메서드 바인딩을 위해 사용하는 'BEFORE'예제에서 코드는 이미 RelayCommand을 사용하여 최적화되었습니다. 마지막으로 확인한 코드는 WPF의 기본 부분이 아닙니다. 그것 없이는 'BEFORE'사례는 훨씬 더 길었을 것입니다.

13
Luis Perez

Silverlight에 대한 이러한 생각을 가로막는 사람들에게는 다음과 같은 점에 유의할 가치가 있습니다.

Silverlight는 이러한 명령 중 하위 집합 만 제공합니다.

13
Matthew Black

유용한 비트와 조각들 :

코드에서 주로 수행하는 방법은 다음과 같습니다.

Binding b = new Binding();
b.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, this.GetType(), 1);
b.Path = new PropertyPath("MyElementThatNeedsBinding");
MyLabel.SetBinding(ContentProperty, b);

나는 주로코드에서 Behind를 Binding Relative Source로 복사했다.

또한 MSDN 페이지는 예제와 같이 꽤 훌륭합니다 :RelativeSource Class

12
Nathan Cooper

방금 다른 솔루션 나를 위해 작동하는 Silverlight에서 부모 요소의 DataContext에 액세스하기위한 게시했습니다. 그것은 Binding ElementName를 사용합니다.

10
Juve

모든 대답을 읽지는 않았지만 버튼의 상대 소스 명령 바인딩의 경우이 정보를 추가하기 만하면됩니다.

Mode=FindAncestor와 함께 상대 소스를 사용하는 경우 바인딩은 다음과 같아야합니다.

Command="{Binding Path=DataContext.CommandProperty, RelativeSource={...}}"

경로에 DataContext를 추가하지 않으면 실행시 속성을 검색 할 수 없습니다.

9
Kevin VDF

이것은 비어있는 DataGrid에서 저에게 효과가 있었던이 패턴의 사용 예입니다.

<Style.Triggers>
    <DataTrigger Binding="{Binding Items.Count, RelativeSource={RelativeSource Self}}" Value="0">
        <Setter Property="Background">
            <Setter.Value>
                <VisualBrush Stretch="None">
                    <VisualBrush.Visual>
                        <TextBlock Text="We did't find any matching records for your search..." FontSize="16" FontWeight="SemiBold" Foreground="LightCoral"/>
                    </VisualBrush.Visual>
                </VisualBrush>
            </Setter.Value>
        </Setter>
    </DataTrigger>
</Style.Triggers>
8
Edd

요소가 시각적 트리의 일부가 아닌 경우 RelativeSource는 작동하지 않습니다.

이 경우 Thomas Levesque가 개척 한 다른 기술을 시도해야합니다.

그는 자신의 블로그에서 [WPF] DataContext가 상속되지 않았을 때 데이터에 바인딩하는 방법 솔루션을 제공합니다. 그리고 그것은 절대적으로 훌륭하게 작동합니다!

드물 긴하지만 자신의 블로그가 다운 된 경우 부록 A에 그의 기사 미러 사본이 있습니다.

여기에 의견을 남기지 말고, 그의 블로그 게시물에 직접 코멘트하십시오 .

부록 A : 블로그 게시물의 거울

WPF의 DataContext 속성은 할당 된 요소의 모든 자식에 의해 자동으로 상속되므로 매우 편리합니다. 따라서 바인딩하려는 각 요소에 다시 설정할 필요가 없습니다. 그러나 경우에 따라 DataContext에 액세스 할 수 없습니다. 시각적 트리 또는 논리 트리의 일부가 아닌 요소에 대해 발생합니다. 그 요소에 속성을 바인딩하는 것은 매우 어려울 수 있습니다 ...

간단한 예를 들어 설명해 드리겠습니다. DataGrid에 제품 목록을 표시하려고합니다. 표에서 우리는 ViewModel에 의해 노출 된 ShowPrice 속성의 값을 기반으로 Price 열을 표시하거나 숨길 수 있어야합니다. 분명한 접근법은 열의 가시성을 ShowPrice 속성에 바인딩하는 것입니다.

<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False"
                Visibility="{Binding ShowPrice,
                Converter={StaticResource visibilityConverter}}"/>

불행히도 ShowPrice의 값을 변경해도 아무 효과가 없으며 열은 항상 표시됩니다 ... 왜? Visual Studio의 출력 창을 보면 다음과 같은 줄을 알 수 있습니다.

System.Windows.Data 오류 : 2 : 대상 요소에 대해 FrameworkElement 또는 FrameworkContentElement를 찾을 수 없습니다. BindingExpression : Path = ShowPrice; DataItem = null; 대상 요소는 'DataGridTextColumn'(HashCode = 32685253)입니다. 대상 속성이 '가시성'(유형 '가시성')입니다.

메시지는 다소 암시 적이지만 의미는 실제로 간단합니다. WPF는 DataContext를 가져 오는 데 사용할 FrameworkElement를 알지 못합니다. 열은 DataGrid의 시각적 트리 또는 논리 트리에 속하지 않기 때문입니다.

예를 들어 RelativeSource를 DataGrid 자체로 설정하여 바인딩을 조정하여 원하는 결과를 얻을 수 있습니다.

<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False"
                Visibility="{Binding DataContext.ShowPrice,
                Converter={StaticResource visibilityConverter},
                RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}}"/>

또는 ShowParice에 바인딩 된 CheckBox를 추가하고 요소 이름을 지정하여 열 가시성을 IsChecked 속성에 바인딩 할 수 있습니다.

<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False"
                Visibility="{Binding IsChecked,
                Converter={StaticResource visibilityConverter},
                ElementName=chkShowPrice}"/>

그러나 이러한 해결 방법 중 어느 것도 작동하지 않는 것으로 보입니다. 우리는 항상 동일한 결과를 얻습니다.

이 시점에서 유일한 실행 가능한 접근법은 MVVM 패턴을 사용할 때 일반적으로 피하는 코드 숨김에서 열의 가시성을 변경하는 것일 것 같습니다 ... 그러나 그렇게 빨리 포기하지는 않을 것입니다. 고려해야 할 다른 옵션이 있습니까?

우리의 문제에 대한 해결책은 실제로 매우 간단하며 Freezable 클래스를 이용합니다. 이 클래스의 주된 목적은 수정 가능하고 읽기 전용 상태 인 객체를 정의하는 것이지만이 경우에는 Freezable 객체가 시각적 트리 또는 논리 트리가 아닌 경우에도 Freezable 객체가 DataContext를 상속받을 수 있다는 점이 흥미 롭습니다. 나는이 행동을 가능하게하는 정확한 메커니즘을 모르지만, 우리는 그것을 구속력있는 작업으로 만들기 위해 ...

아이디어는 Freezable을 상속하고 데이터 종속성 속성을 선언하는 클래스를 작성하는 것입니다 (이유는 BindingProxy라고합니다).

public class BindingProxy : Freezable
{
    #region Overrides of Freezable

    protected override Freezable CreateInstanceCore()
    {
        return new BindingProxy();
    }

    #endregion

    public object Data
    {
        get { return (object)GetValue(DataProperty); }
        set { SetValue(DataProperty, value); }
    }

    // Using a DependencyProperty as the backing store for Data.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty DataProperty =
        DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
}

그런 다음 DataGrid의 리소스에서이 클래스의 인스턴스를 선언하고 Data 속성을 현재 DataContext에 바인딩 할 수 있습니다.

<DataGrid.Resources>
    <local:BindingProxy x:Key="proxy" Data="{Binding}" />
</DataGrid.Resources>

마지막 단계는 BindingProxy 객체 (StaticResource로 쉽게 액세스 할 수 있음)를 바인딩 소스로 지정하는 것입니다.

<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False"
                Visibility="{Binding Data.ShowPrice,
                Converter={StaticResource visibilityConverter},
                Source={StaticResource proxy}}"/>

경로는 이제 BindingProxy 객체를 기준으로하므로 바인딩 경로의 접두사는 "Data"입니다.

이제 바인딩이 제대로 작동하고 ShowPrice 속성에 따라 열이 제대로 표시되거나 숨겨집니다.

4
Contango