답변:
객체의 다른 속성에 바인딩하려는 경우 :
{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}
AncestorType
.
FindAncestor
전에를 생략하면 AncestorType
"RelativeSource가 FindAncestor 모드에 있지 않습니다"라는 오류가 발생합니다. (VS2013, 커뮤니티 버전)
{Binding Path=DataContext.SomeProperty, RelativeSource=...
. DataTemplate 내에서 부모의 DataContext에 바인딩하려고 할 때 초보자에게는 다소 예기치 않은 일이었습니다.
Binding RelativeSource={
RelativeSource Mode=FindAncestor, AncestorType={x:Type ItemType}
}
...
의 기본 속성 RelativeSource
은 IS Mode
속성입니다. MSDN에서 제공하는 유효한 값의 전체 세트는 다음과 같습니다 .
PreviousData 표시되는 데이터 항목 목록에서 이전 데이터 항목 (데이터 항목이 포함 된 컨트롤이 아님)을 바인딩 할 수 있습니다.
TemplatedParent 템플릿 (데이터 바인딩 된 요소가있는)이 적용되는 요소를 나타냅니다. 이는 TemplateBindingExtension 설정과 유사하며 바인딩이 템플리트 내에있는 경우에만 적용 가능합니다.
자체 바인딩을 설정하는 요소를 참조하고 해당 요소의 한 속성을 같은 요소의 다른 속성에 바인딩 할 수 있습니다.
FindAncestor 데이터 바인딩 된 요소의 부모 체인에있는 조상을 나타냅니다. 이를 사용하여 특정 유형의 조상 또는 해당 서브 클래스에 바인딩 할 수 있습니다. AncestorType 및 / 또는 AncestorLevel을 지정하려는 경우 사용하는 모드입니다.
다음은 MVVM 아키텍처와 관련하여보다 시각적 인 설명입니다.
{Binding Message}
(조금 더 간단하다 ...)
Path=DataContext.Message
바인딩이 작동 하도록 명시 적으로 설정 해야했습니다. 너비 / 높이 / 등에 상대 바인딩을 수행 할 수 있다는 점에서 이치에 맞습니다. 통제의.
Bechir Bejaoui는 그의 기사 에서 WPF의 RelativeSources 사용 사례를 공개합니다 .
RelativeSource는 특정 객체의 속성을 객체 자체의 다른 속성에 바인딩하려고 할 때, 특정 객체의 속성을 상대 부모 중 하나에 바인딩하려고 할 때 특히 바인딩 케이스에 사용되는 태그 확장입니다. 사용자 지정 컨트롤 개발 및 일련의 바운드 데이터 차등을 사용하는 경우 종속성 속성 값을 XAML에 바인딩 할 때 이러한 모든 상황은 상대 소스 모드로 표시됩니다. 모든 사례를 하나씩 공개하겠습니다.
- 모드 자체 :
높이가 항상 너비와 같기를 원하는 사각형 인이 경우를 상상해보십시오. 우리는 요소 이름을 사용 하여이 작업을 수행 할 수 있습니다
<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}"/>
이 경우 바인딩 객체의 이름을 언급 할 의무가 없으며 높이가 변경 될 때마다 너비는 항상 높이와 같습니다.
너비를 높이의 절반으로 매개 변수화하려면 바인딩 태그 확장에 변환기를 추가하여이 작업을 수행 할 수 있습니다. 다른 경우를 상상해 봅시다.
<TextBlock Width="{Binding RelativeSource={RelativeSource Self}, Path=Parent.ActualWidth}"/>
위의 경우는 주어진 요소의 주어진 속성을 직접 부모의 속성 중 하나에 묶는 데 사용됩니다.이 요소에는 Parent라는 속성이 있습니다. 이것은 다른 상대 소스 모드 인 FindAncestor 모드로 연결됩니다.
- 모드 찾기
이 경우 주어진 요소의 속성은 부모 중 하나 인 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로 변경하고 어떻게되는지보십시오.
표시되는 텍스트는 조상 유형 및 레벨에 따라 변경됩니다. 그러면 조상 수준이 조상 유형에 적합하지 않으면 어떻게됩니까? 좋은 질문입니다. 물어 보려고한다는 것을 알고 있습니다. 응답은 예외가 발생하지 않으며 TextBlock 수준에 아무것도 표시되지 않습니다.
- 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 인 TemplateBinding이지만, 첫 번째 런타임 직후에 평가되는 TemplatedParent와 대조적으로 컴파일 타임에 TemplateBinding이 평가됩니다. 아래 그림에서 알 수 있듯이 배경과 내용은 단추 내에서 컨트롤 템플릿에 적용됩니다.
ListView
. 부모는 그 ListView
아래에 2 단계 더 있습니다. 이 날 각의 이후의 각 VM에 데이터를 전달 방지 도움 ListView
의DataTemplate
WPF RelativeSource
바인딩 properties
에서 다음을 설정하기 위해 3 을 노출합니다 .
1. 모드 : 다음과 같은 enum
네 가지 값을 가질 수 있습니다.
ㅏ. PreviousData (
value=0
) : 의 이전 값을property
바인딩 된값에 할당합니다비. TemplatedParent (
value=1
) :templates
컨트롤을 정의 할 때 사용되며의 값 / 속성에 바인딩하려고합니다control
.예를 들어, 다음을 정의하십시오
ControlTemplate
.
<ControlTemplate>
<CheckBox IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</ControlTemplate>
씨. 자기 (
value=2
) : 우리가에서 바인딩 할 때self
또는property
자기의.예를 들면 다음 과
checkbox
같습니다.CommandParameter
Command
켜기 를 설정하는 동안CheckBox
<CheckBox ...... CommandParameter="{Binding RelativeSource={RelativeSource Self},Path=IsChecked}" />
디. FindAncestor (
value=3
) : 부모로부터 바인딩 할 때control
에서Visual Tree
.예를 들어 바인딩
checkbox
에서records
만약grid
, 경우는header
checkbox
체크
<CheckBox IsChecked="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type iDP:XamDataGrid}}, Path=DataContext.IsHeaderChecked, Mode=TwoWay}" />
2. 조상 유형 : mode가 FindAncestor
어떤 조상 유형을 정의 할 때
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type iDP:XamDataGrid}}
3. 조상 레벨 : mode가 FindAncestor
조상 레벨 인 경우 (에 동일한 유형의 상위가 두 개있는 경우 visual tree
)
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type iDP:XamDataGrid, AncestorLevel=1}}
위의 모든 사용 사례는
RelativeSource binding
.
Silverlight에 대한 이러한 생각에 걸려 넘어지는 사람들에게는 다음과 같은 점에 주목할 가치가 있습니다.
Silverlight는 이러한 명령 중 축소 된 하위 집합 만 제공합니다.
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'예제가 더 길었을 것입니다.
유용한 비트 및 조각 :
코드에서 주로 수행하는 방법은 다음과 같습니다.
Binding b = new Binding();
b.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, this.GetType(), 1);
b.Path = new PropertyPath("MyElementThatNeedsBinding");
MyLabel.SetBinding(ContentProperty, b);
나는 이것을 코드 숨김의 바인딩 상대 소스에서 크게 복사했습니다. .
또한 MSDN 페이지는 예제가있는 한 꽤 좋습니다. RelativeSource Class
이것은 빈 데이터 그리드에서 나를 위해 일한이 패턴의 사용 예입니다.
<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>
요소가 비주얼 트리의 일부가 아닌 경우 RelativeSource는 작동하지 않습니다.
이 경우 Thomas Levesque가 개척 한 다른 기술을 시도해야합니다.
그는 블로그에서 [WPF] 아래에 솔루션을 가지고 있습니다. 있습니다. 그리고 그것은 절대적으로 훌륭하게 작동합니다!
그의 블로그가 다운되지 않았을 경우, 부록 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 : 경로 = 쇼 가격; DataItem = null; 대상 요소는 'DataGridTextColumn'(HashCode = 32685253)입니다. 대상 속성은 'Visibility'(유형 'Visibility')입니다.
메시지는 다소 비밀 스럽지만 실제로는 그 의미가 매우 간단합니다. WPF는 열이 DataGrid의 시각적 또는 논리 트리에 속하지 않기 때문에 DataContext를 가져 오는 데 사용할 FrameworkElement를 알지 못합니다.
예를 들어 RelativeSource를 DataGrid 자체로 설정하여 원하는 결과를 얻도록 바인딩을 조정할 수 있습니다.
<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False"
Visibility="{Binding DataContext.ShowPrice,
Converter={StaticResource visibilityConverter},
RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}}"/>
또는 ShowPrice에 바인딩 된 CheckBox를 추가하고 요소 이름을 지정하여 열 가시성을 IsChecked 속성에 바인딩 할 수 있습니다.
<DataGridTextColumn Header="Price" Binding="{Binding Price}" IsReadOnly="False"
Visibility="{Binding IsChecked,
Converter={StaticResource visibilityConverter},
ElementName=chkShowPrice}"/>
그러나이 해결 방법 중 어느 것도 효과가없는 것 같습니다. 항상 같은 결과를 얻습니다.
이 시점에서 유일하게 실행 가능한 접근 방식은 코드 숨김에서 열 가시성을 변경하는 것 같습니다. MVVM 패턴을 사용할 때 일반적으로 피하는 것이 좋습니다 ... 그러나 적어도 그렇게 빨리 포기하지는 않을 것입니다. 고려해야 할 다른 옵션이 있지만 😉
우리의 문제에 대한 해결책은 실제로 매우 간단하며 Freezable 클래스를 활용합니다. 이 클래스의 기본 목적은 수정 가능하고 읽기 전용 상태 인 객체를 정의하는 것이지만, 우리의 흥미로운 기능은 시각적 또는 논리적 트리에 있지 않은 경우에도 Freezable 객체가 DataContext를 상속 할 수 있다는 것입니다. 이 동작을 가능하게하는 정확한 메커니즘을 모르지만 바인딩 작업을 위해이를 활용할 것입니다.
아이디어는 Freezable을 상속하고 데이터 종속성 속성을 선언하는 클래스를 작성하는 것입니다.
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 속성에 따라 열이 올바르게 표시되거나 숨겨집니다.