Làm cách nào để sử dụng các ràng buộc WPF với RelativeSource?


Câu trả lời:


783

Nếu bạn muốn liên kết với một thuộc tính khác trên đối tượng:

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

Nếu bạn muốn có một tài sản trên tổ tiên:

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

Nếu bạn muốn có một thuộc tính trên cha mẹ templated (vì vậy bạn có thể thực hiện các ràng buộc 2 chiều trong ControlTemplate)

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

hoặc, ngắn hơn (điều này chỉ hoạt động cho các ràng buộc OneWay):

{TemplateBinding Path=PathToProperty}

15
Đối với cái này "{Binding Path = PathToProperty, RelativeSource = {RelativeSource AneoporType = {x: Type typeOfAncestor}}}", có vẻ như nó cần phải có "Mode = FindAncestor", trước "
AncomborType

1
Vì công nghệ gì? Trong WPF, điều đó được suy ra khi bạn chỉ định một AncestorType.
Abe Heidebrecht

2
Tôi đồng ý với @EdwardM. Khi tôi bỏ qua FindAncestor, trước đó AncestorType, tôi gặp lỗi sau: "RelativeSource không ở chế độ FindAncestor". (Trong VS2013, phiên bản Cộng đồng)
kmote

1
@kmote, điều này đã làm việc cho tôi kể từ .net 3.0 và một lần nữa tôi đã xác minh rằng nó hoạt động theo cách này trong kaxaml ... Một lần nữa, bạn đang sử dụng công nghệ gì? Bộ xử lý XAML khác với WPF / Silverlight / UWP, do đó bạn có thể có kết quả khác nhau trên các công nghệ khác nhau. Bạn cũng đã đề cập đến Cộng đồng VS, vì vậy có thể đó là một cảnh báo IDE, nhưng hoạt động trong thời gian chạy?
Abe Heidebrecht

6
Chỉ muốn lưu ý ở đây rằng nếu bạn muốn liên kết với một thuộc tính trong DataContext của RelativeSource thì bạn phải xác định rõ ràng nó : {Binding Path=DataContext.SomeProperty, RelativeSource=.... Điều này hơi bất ngờ đối với tôi khi là người mới khi tôi đang cố gắng liên kết với DataContext của cha mẹ trong DataTemplate.
DrEsperanto

133
Binding RelativeSource={
    RelativeSource Mode=FindAncestor, AncestorType={x:Type ItemType}
}
...

Thuộc tính mặc định của RelativeSourcelà thuộc Modetính. Một bộ đầy đủ các giá trị hợp lệ được đưa ra ở đây ( từ MSDN ):

  • BeforeData Cho phép bạn liên kết mục dữ liệu trước đó (không phải điều khiển có chứa mục dữ liệu) trong danh sách các mục dữ liệu được hiển thị.

  • TemplatedParent Đề cập đến phần tử mà mẫu (trong đó phần tử ràng buộc dữ liệu tồn tại) được áp dụng. Điều này tương tự với việc đặt TemplateBindingExtension và chỉ được áp dụng nếu Binding nằm trong một mẫu.

  • Tự Tham chiếu đến phần tử mà bạn đang đặt ràng buộc và cho phép bạn liên kết một thuộc tính của phần tử đó với thuộc tính khác trên cùng một phần tử.

  • FindAncestor Đề cập đến tổ tiên trong chuỗi cha của phần tử ràng buộc dữ liệu. Bạn có thể sử dụng điều này để liên kết với tổ tiên của một loại cụ thể hoặc các lớp con của nó. Đây là chế độ bạn sử dụng nếu bạn muốn chỉ định AneoporType và / hoặc AneoporLevel.


128

Đây là một lời giải thích trực quan hơn trong bối cảnh của kiến ​​trúc MVVM:

nhập mô tả hình ảnh ở đây


19
tôi có bỏ lỡ điều gì không? Làm thế nào bạn có thể coi đó là một đồ họa đơn giản và rõ ràng? 1: các hộp bên trái có nghĩa là không thực sự liên quan đến các hộp bên phải (tại sao lại có tệp .cs bên trong ViewModel?) 2: tại các mũi tên DataContext này làm gì? 3: tại sao thuộc tính Message không có trong ViewModel1? và quan trọng nhất là 5: Tại sao bạn cần Liên kết nguồn tương đối để truy cập DataContext của Window nếu TextBlock đã có cùng DataContext đó? Tôi rõ ràng đang thiếu một cái gì đó ở đây vì vậy tôi khá ngu ngốc hoặc đồ họa này không đơn giản và rõ ràng như mọi người nghĩ! Xin hãy soi sáng cho tôi
Markus Hütter

2
@ MarkusHütter Sơ đồ đang hiển thị một nhóm Chế độ xem lồng nhau và ViewModels tương ứng. DataContext của View1 là ViewModel1, nhưng nó muốn liên kết với một thuộc tính của BaseViewModel. Vì BaseViewModel là DataContext của BaseView (là một Cửa sổ), nên nó có thể làm như vậy bằng cách tìm bộ chứa cha mẹ đầu tiên là Cửa sổ và lấy DataContext của nó.
mcargille

6
@MatthewCargille Tôi biết rất rõ ý nghĩa của nó , đó không phải là quan điểm của tôi. Nhưng hãy đặt mình vào vị trí của một người không biết rõ về XAML và MVVM và bạn sẽ thấy rằng điều này không đơn giản và rõ ràng .
Markus Hütter

1
Tôi phải đồng ý với @ MarkusHütter, nhân tiện, ràng buộc bên trái có thể đơn giản như thế này: {Binding Message}(đơn giản hơn một chút ...)
florien

@florien Tôi không nghĩ vậy, ít nhất là cho trường hợp sử dụng của tôi. Tôi có một DataTemplate cần tham khảo DataContext của MainWindow (lớp viewmodel của tôi) để có danh sách các tùy chọn cho menu thả xuống (được tải từ cơ sở dữ liệu). DataTemplate được liên kết với một đối tượng mô hình cũng được tải từ cơ sở dữ liệu, nhưng nó chỉ có quyền truy cập vào tùy chọn đã chọn. Tôi đã phải thiết lập rõ ràng Path=DataContext.Messageđể có được ràng buộc để làm việc. Điều này có ý nghĩa, cho rằng bạn có thể thực hiện các ràng buộc tương đối với chiều rộng / chiều cao / vv. của một điều khiển.
DrEsperanto

47

Bechir Bejaoui trưng bày các trường hợp sử dụng của RelativeSource trong WPF trong bài viết của mình tại đây :

RelativeSource là một phần mở rộng đánh dấu được sử dụng trong các trường hợp ràng buộc cụ thể khi chúng ta cố gắng liên kết một thuộc tính của một đối tượng nhất định với một thuộc tính khác của chính đối tượng đó, khi chúng ta cố gắng liên kết một thuộc tính của đối tượng với một đối tượng cha mẹ tương đối của nó, khi ràng buộc một giá trị thuộc tính phụ thuộc vào một phần XAML trong trường hợp phát triển điều khiển tùy chỉnh và cuối cùng trong trường hợp sử dụng vi sai của một chuỗi dữ liệu bị ràng buộc. Tất cả các tình huống đó được thể hiện dưới dạng chế độ nguồn tương đối. Tôi sẽ phơi bày tất cả các trường hợp một.

  1. Chế độ tự:

Hãy tưởng tượng trường hợp này, một hình chữ nhật mà chúng ta muốn rằng chiều cao của nó luôn bằng chiều rộng của nó, một hình vuông hãy nói. Chúng ta có thể làm điều này bằng cách sử dụng tên thành phần

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

Nhưng trong trường hợp trên, chúng tôi có nghĩa vụ phải chỉ ra tên của đối tượng ràng buộc, cụ thể là hình chữ nhật. Chúng ta có thể đạt được cùng một mục đích khác nhau bằng cách sử dụng RelativeSource

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

Trong trường hợp đó, chúng tôi không bắt buộc phải đề cập đến tên của đối tượng ràng buộc và Chiều rộng sẽ luôn bằng với Chiều cao mỗi khi thay đổi chiều cao.

Nếu bạn muốn tham số Width là một nửa chiều cao thì bạn có thể làm điều này bằng cách thêm một trình chuyển đổi vào phần mở rộng đánh dấu Binding. Bây giờ hãy tưởng tượng một trường hợp khác:

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

Trường hợp trên được sử dụng để buộc một thuộc tính nhất định của một phần tử đã cho với một trong các phần tử cha mẹ trực tiếp của nó vì phần tử này giữ một thuộc tính được gọi là Parent. Điều này dẫn chúng ta đến một chế độ nguồn tương đối khác, đó là chế độ FindAncestor.

  1. Chế độ FindAncestor

Trong trường hợp này, một tài sản của một yếu tố nhất định sẽ được gắn với một trong những cha mẹ của nó, Of Corse. Sự khác biệt chính với trường hợp trên là ở chỗ, tùy thuộc vào bạn để xác định loại tổ tiên và thứ hạng tổ tiên trong hệ thống phân cấp để buộc tài sản. Nhân tiện, hãy thử chơi với đoạn XAML này

<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>

Tình huống trên là của hai phần tử TextBlock được nhúng trong một loạt các phần tử viền và phần tử canvas đại diện cho cha mẹ phân cấp của chúng. TextBlock thứ hai sẽ hiển thị tên của cha mẹ đã cho ở mức nguồn tương đối.

Vì vậy, hãy thử thay đổi AneoporLevel = 2 thành AneoporLevel = 1 và xem điều gì sẽ xảy ra. Sau đó thử thay đổi loại của tổ tiên từ AneoporType = Border thành AneoporType = Canvas và xem điều gì sẽ xảy ra.

Văn bản được hiển thị sẽ thay đổi theo loại và cấp độ Tổ tiên. Vậy thì điều gì sẽ xảy ra nếu cấp độ tổ tiên không phù hợp với loại tổ tiên? Đây là một câu hỏi hay, tôi biết rằng bạn sắp hỏi nó. Phản hồi không có ngoại lệ sẽ được đưa ra và các thông báo sẽ được hiển thị ở cấp độ TextBlock.

  1. TemplatedParent

Chế độ này cho phép buộc một thuộc tính ControlTemplate đã cho vào một thuộc tính của điều khiển mà ControlTemplate được áp dụng. Để hiểu rõ vấn đề ở đây là một ví dụ dưới đây

<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>

Nếu tôi muốn áp dụng các thuộc tính của một điều khiển nhất định cho mẫu điều khiển của nó thì tôi có thể sử dụng chế độ TemplatedParent. Ngoài ra còn có một tiện ích tương tự với tiện ích mở rộng đánh dấu này, đó là TemplateBinding, một loại tay ngắn của cái đầu tiên, nhưng TemplateBinding được đánh giá tại thời điểm biên dịch theo độ tương phản của TemplatedParent được đánh giá ngay sau lần chạy đầu tiên. Như bạn có thể nhận xét trong hình dưới đây, nền và nội dung được áp dụng từ bên trong nút cho mẫu điều khiển.


Các ví dụ rất hay đối với tôi, đã sử dụng Tìm tổ tiên để liên kết với một lệnh trong ngữ cảnh dữ liệu của cha mẹ ListView. Phụ huynh có thêm 2 ListViewcấp độ bên dưới nó. Điều này đã giúp tôi ngăn chặn thông qua dữ liệu vào mỗi vm tiếp theo của mỗi ListView'sDataTemplate
Caleb W.

34

Trong WPF RelativeSourceràng buộc hiển thị ba propertiesđể thiết lập:

1. Chế độ: Đây là một enumgiá trị có thể có bốn giá trị:

a. BeforeData ( value=0): Nó gán giá trị trước đó cho giá trịpropertyràng buộc

b. TemplatedParent ( value=1): Điều này được sử dụng khi xác địnhtemplatesbất kỳ điều khiển nào và muốn liên kết với một giá trị / Thuộc tính củacontrol.

Ví dụControlTemplate : xác định :

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

c. Self ( value=2): Khi chúng ta muốn ràng buộc từ mộtselfhoặc mộtpropertycái tôi.

Ví dụ: Gửi trạng thái được kiểm tra checkboxnhư CommandParametertrong khi cài đặt CommandbậtCheckBox

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

d. FindAncestor ( value=3): Khi muốn liên kết từ cha mẹcontrol trongVisual Tree.

Ví dụ: Ràng buộc a checkboxtrong recordsnếu a grid, nếu header checkboxđược chọn

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

2. AneoporType: khi chế độ được FindAncestorxác định loại tổ tiên

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

3. AneoporLevel: khi chế độ làFindAncestorcấp độ của tổ tiên (nếu có hai loại cha mẹ giống nhauvisual tree)

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

Trên đây là tất cả các trường hợp sử dụng cho RelativeSource binding.

Đây là một liên kết tham khảo .


2
Thật tuyệt vời , Mode = OneWay} "/> nơi tôi đang cố gắng liên kết với tài sản được chọn của cửa sổ cha mẹ. Tài sản được trả tiền
Michael K

21

Đừng quên TemplatedParent:

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

hoặc là

{Binding RelativeSource={RelativeSource TemplatedParent}}


16

Tôi đã tạo một thư viện để đơn giản hóa cú pháp ràng buộc của WPF bao gồm việc sử dụng RelativeSource dễ dàng hơn. Dưới đây là một số ví dụ. Trước:

{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}

Sau:

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

Dưới đây là một ví dụ về cách liên kết phương thức được đơn giản hóa. Trước:

// 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}

Sau:

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

// XAML
{BindTo SaveObject()}

Bạn có thể tìm thấy thư viện ở đây: http://www.simplygoodcode.com/2012/08/simpler-wpf-binding.html

Lưu ý trong ví dụ 'TRƯỚC' mà tôi sử dụng để liên kết phương thức mà mã đã được tối ưu hóa bằng cách sử dụng RelayCommandcái cuối cùng tôi đã kiểm tra không phải là một phần nguyên gốc của WPF. Nếu không có điều đó, ví dụ 'TRƯỚC' sẽ còn dài hơn nữa.


2
Những bài tập cầm tay này thể hiện sự yếu kém của XAML; cách quá phức tạp.
dudeNumber4

16

Một số bit và phần hữu ích:

Đây là cách để làm điều đó chủ yếu trong mã:

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

Tôi đã sao chép phần lớn từ Binding Relative Source trong mã Phía sau .

Ngoài ra, trang MSDN khá tốt theo như ví dụ: Lớp RelativeSource


5
Ký ức mơ hồ của tôi về WPF là việc thực hiện các ràng buộc trong mã có lẽ thường không phải là điều tốt nhất.
Nathan Cooper

12

Tôi vừa đăng một giải pháp khác để truy cập DataContext của một phần tử cha trong Silverlight phù hợp với tôi. Nó sử dụng Binding ElementName.


10

Tôi đã không đọc mọi câu trả lời, nhưng tôi chỉ muốn thêm thông tin này trong trường hợp ràng buộc lệnh nguồn tương đối của một nút.

Khi bạn sử dụng một nguồn tương đối với Mode=FindAncestor, ràng buộc phải như sau:

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

Nếu bạn không thêm DataContext vào đường dẫn của mình, tại thời điểm thực thi, nó không thể truy xuất thuộc tính.


9

Đây là một ví dụ về việc sử dụng mẫu này hoạt động với tôi trên các bảng dữ liệu trống.

<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>

6

Nếu một phần tử không phải là một phần của cây trực quan, thì RelativeSource sẽ không bao giờ hoạt động.

Trong trường hợp này, bạn cần thử một kỹ thuật khác, được tiên phong bởi Thomas Levesque.

Anh ta có giải pháp trên blog của mình trong [WPF] Cách liên kết với dữ liệu khi DataContext không được kế thừa . Và nó hoạt động hoàn toàn rực rỡ!

Trong trường hợp không chắc là blog của anh ta bị sập, Phụ lục A chứa bản sao phản chiếu bài viết của anh ta .

Xin vui lòng không bình luận ở đây, xin vui lòng bình luận trực tiếp trên bài viết blog của mình .

Phụ lục A: Gương của bài đăng trên blog

Thuộc tính DataContext trong WPF cực kỳ tiện dụng, bởi vì nó được tự động kế thừa bởi tất cả các phần tử con mà bạn gán nó; do đó bạn không cần phải đặt lại trên mỗi thành phần bạn muốn liên kết. Tuy nhiên, trong một số trường hợp, DataContext không thể truy cập được: nó xảy ra đối với các phần tử không phải là một phần của cây trực quan hoặc logic. Sau đó có thể rất khó khăn để ràng buộc một tài sản trên các yếu tố đó.

Hãy minh họa bằng một ví dụ đơn giản: chúng tôi muốn hiển thị danh sách các sản phẩm trong DataGrid. Trong lưới, chúng tôi muốn có thể hiển thị hoặc ẩn cột Giá, dựa trên giá trị của thuộc tính Showprice được hiển thị bởi ViewModel. Cách tiếp cận rõ ràng là liên kết Khả năng hiển thị của cột với thuộc tính Showprice:

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

Thật không may, việc thay đổi giá trị của Showprice không có hiệu lực và cột luôn hiển thị, vì sao? Nếu chúng ta nhìn vào cửa sổ đầu ra trong Visual Studio, chúng ta sẽ thấy dòng sau:

System.Windows.Data Lỗi: 2: Không thể tìm thấy quản lý FrameworkEuity hoặc FrameworkContentE bổ sung cho phần tử đích. BindingExpression: Đường dẫn = Showprice; DataItem = null; phần tử đích là 'DataGridTextColumn' (HashCode = 32685253); thuộc tính mục tiêu là 'Tầm nhìn' (loại 'Tầm nhìn')

Thông điệp này khá khó hiểu, nhưng ý nghĩa thực sự khá đơn giản: WPF không biết sử dụng FrameworkEuity nào để lấy DataContext, vì cột không thuộc về cây trực quan hoặc logic của DataGrid.

Ví dụ, chúng ta có thể cố gắng điều chỉnh liên kết để có được kết quả mong muốn bằng cách đặt RelativeSource thành DataGrid:

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

Hoặc chúng ta có thể thêm một CheckBox bị ràng buộc vào Showprice và cố gắng liên kết mức độ hiển thị của cột với thuộc tính IsChecked bằng cách chỉ định tên thành phần:

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

Nhưng dường như không có cách giải quyết nào có hiệu quả, chúng tôi luôn nhận được kết quả tương tự

Tại thời điểm này, dường như cách tiếp cận khả thi duy nhất là thay đổi mức độ hiển thị của cột theo mã phía sau, điều mà chúng ta thường muốn tránh khi sử dụng mẫu MVVM, Nhưng tôi sẽ không từ bỏ sớm, ít nhất là không trong khi có những lựa chọn khác để xem xét

Giải pháp cho vấn đề của chúng tôi thực sự khá đơn giản và tận dụng lớp Freezable. Mục đích chính của lớp này là xác định các đối tượng có trạng thái có thể sửa đổi và chỉ đọc, nhưng tính năng thú vị trong trường hợp của chúng ta là các đối tượng Freezable có thể kế thừa DataContext ngay cả khi chúng không ở trong cây trực quan hoặc logic. Tôi không biết cơ chế chính xác cho phép thực hiện hành vi này, nhưng chúng tôi sẽ tận dụng lợi thế của nó để thực hiện công việc ràng buộc của chúng tôi

Ý tưởng là tạo ra một lớp (tôi gọi nó là BindingProxy vì những lý do sẽ sớm trở nên rõ ràng) kế thừa Freezable và tuyên bố một thuộc tính phụ thuộc dữ liệu:

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));
}

Sau đó chúng ta có thể khai báo một thể hiện của lớp này trong các tài nguyên của DataGrid và liên kết thuộc tính Dữ liệu với DataContext hiện tại:

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

Bước cuối cùng là chỉ định đối tượng BindingProxy này (có thể truy cập dễ dàng bằng StaticResource) làm Nguồn cho liên kết:

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

Lưu ý rằng đường dẫn liên kết đã được thêm tiền tố với dữ liệu dữ liệu, vì đường dẫn này hiện có liên quan đến đối tượng BindingProxy.

Liên kết bây giờ hoạt động chính xác và cột được hiển thị hoặc ẩn chính xác dựa trên thuộc tính Showprice.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.