2010-08-04 40 views
84

Tôi có một số ListBox liên kết với bộ sưu tập con trên ViewModel. Các mặt hàng listbox được theo kiểu trong một datatemplate dựa trên một tài sản trên ViewModel mẹ:Truy cập dữ liệu gốc của DataContext từ DataTemplate

<Style x:Key="curveSpeedNonConstantParameterCell"> 
    <Style.Triggers> 
     <DataTrigger Binding="{Binding Path=DataContext.CurveSpeedMustBeSpecified, 
      ElementName=someParentElementWithReferenceToRootDataContext}" 
      Value="True"> 
      <Setter Property="Control.Visibility" Value="Hidden"></Setter> 
     </DataTrigger> 
    </Style.Triggers> 
</Style> 

tôi nhận được lỗi đầu ra sau đây:

System.Windows.Data Error: 39 : BindingExpression path error: 
'CurveSpeedMustBeSpecified' property not found on 
    'object' ''BindingListCollectionView' (HashCode=20467555)'. 
BindingExpression:Path=DataContext.CurveSpeedMustBeSpecified; 
DataItem='Grid' (Name='nonConstantCurveParametersGrid'); 
target element is 'TextBox' (Name=''); 
target property is 'NoTarget' (type 'Object') 

Vì vậy, nếu tôi thay đổi biểu thức ràng buộc để "Path=DataContext.CurrentItem.CurveSpeedMustBeSpecified" nó hoạt động , nhưng chỉ miễn là datacontext của điều khiển người dùng gốc là BindingListCollectionView. Điều này không được chấp nhận vì phần còn lại của điều khiển người dùng liên kết với các thuộc tính của số CurrentItem trên số BindingList tự động.

Làm cách nào để chỉ định biểu thức ràng buộc bên trong kiểu để nó hoạt động bất kể ngữ cảnh dữ liệu gốc là chế độ xem bộ sưu tập hay một mục duy nhất?

Trả lời

135

Tôi gặp sự cố với nguồn tương đối trong Silverlight. Sau khi tìm kiếm và đọc, tôi không tìm thấy một giải pháp phù hợp mà không cần sử dụng một số thư viện Binding bổ sung. Nhưng, ở đây là một cách tiếp cận khác để giành quyền truy cập vào DataContext phụ huynh bằng cách tham chiếu trực tiếp một yếu tố mà bạn biết ngữ cảnh dữ liệu. Nó sử dụng Binding ElementName và hoạt động khá tốt, miễn là bạn tôn trọng đặt tên riêng của mình và không có tái sử dụng nặng của templates/styles qua phần:

<ItemsControl x:Name="level1Lister" ItemsSource={Binding MyLevel1List}> 
    <ItemsControl.ItemTemplate> 
    <DataTemplate> 
     <Button Content={Binding MyLevel2Property} 
       Command={Binding ElementName=level1Lister, 
         Path=DataContext.MyLevel1Command} 
       CommandParameter={Binding MyLevel2Property}> 
     </Button> 
    <DataTemplate> 
    <ItemsControl.ItemTemplate> 
</ItemsControl> 

này cũng hoạt động nếu bạn đặt nút vào Style/Template:

<Border.Resources> 
    <Style x:Key="buttonStyle" TargetType="Button"> 
    <Setter Property="Template"> 
     <Setter.Value> 
     <ControlTemplate TargetType="Button"> 
      <Button Command={Binding ElementName=level1Lister, 
            Path=DataContext.MyLevel1Command} 
        CommandParameter={Binding MyLevel2Property}> 
       <ContentPresenter/> 
      </Button> 
     </ControlTemplate> 
     </Setter.Value> 
    </Setter> 
    </Style> 
</Border.Resources> 

<ItemsControl x:Name="level1Lister" ItemsSource={Binding MyLevel1List}> 
    <ItemsControl.ItemTemplate> 
    <DataTemplate> 
     <Button Content="{Binding MyLevel2Property}" 
       Style="{StaticResource buttonStyle}"/> 
    <DataTemplate> 
    <ItemsControl.ItemTemplate> 
</ItemsControl> 

Lúc đầu, tôi nghĩ rằng x:Names của các yếu tố cha mẹ không thể truy cập từ bên trong một mục templated, nhưng kể từ khi tôi không tìm thấy giải pháp tốt hơn, tôi chỉ cố gắng, và nó hoạt động tốt.

+0

Tôi có mã chính xác trong dự án của tôi nhưng nó bị rò rỉ ViewModels (Finalizer không được gọi, Command ràng buộc dường như giữ lại DataContext). Bạn có thể xác minh rằng vấn đề này tồn tại cho bạn không? –

+0

@Juve công trình này, nhưng nó có thể làm điều này để nó sẽ cháy cho tất cả các itemscontrols thực hiện cùng một mẫu? Tên là duy nhất vì vậy sau đó chúng tôi sẽ cần một mẫu riêng biệt cho mỗi, trừ khi tôi đang thiếu một cái gì đó. – Chris

+1

@Juve bỏ qua cuối cùng của tôi, tôi đã nhận nó để làm việc bằng cách sử dụng người thân với findancestor và tìm kiếm theo tổ tiên, (vì vậy tất cả như nhau ngoại trừ không tìm kiếm theo tên). Trong trường hợp của tôi, tôi lặp lại việc sử dụng ItemsControls mỗi cái thực hiện một mẫu để tôi trông như thế này: Command = "{Binding RelativeSource = {RelativeSource FindAncestor, AncestorType = {x: Type ItemsControl}}, Path = DataContext.OpenDocumentBtnCommand}" – Chris

37

Bạn có thể sử dụng RelativeSource để tìm các yếu tố phụ huynh, như thế này -

Binding="{Binding Path=DataContext.CurveSpeedMustBeSpecified, 
RelativeSource={RelativeSource AncestorType={x:Type local:YourParentElementType}}}" 

Xem this SO question để biết thêm chi tiết về RelativeSource.

+8

Tôi phải chỉ định 'Chế độ = TìmAncestor' để nó hoạt động, nhưng điều này hoạt động và tốt hơn nhiều trong kịch bản MVVM vì nó tránh điều khiển đặt tên. 'Binding =" {Binding Path = DataContext.CurveSpeedMustBeSpecified, RelativeSource = {RelativeSource Mode = FindAncestor, AncestorType = {x: Gõ địa phương: YourParentElementType}}} "' – Aphex

+1

hoạt động như một nét duyên dáng <3 và không phải chỉ định mode, .net 4.6.1 – user2475096

16

Tôi đã tìm kiếm làm thế nào để làm điều gì đó tương tự như trong WPF và tôi đã nhận giải pháp này:

<ItemsControl ItemsSource="{Binding MyItems,Mode=OneWay}"> 
<ItemsControl.ItemsPanel> 
    <ItemsPanelTemplate> 
     <StackPanel Orientation="Vertical" /> 
    </ItemsPanelTemplate> 
</ItemsControl.ItemsPanel> 
<ItemsControl.ItemTemplate> 
    <DataTemplate> 
     <RadioButton 
      Content="{Binding}" 
      Command="{Binding Path=DataContext.CustomCommand, 
         RelativeSource={RelativeSource Mode=FindAncestor,  
         AncestorType={x:Type ItemsControl}} }" 
      CommandParameter="{Binding}" /> 
    </DataTemplate> 
</ItemsControl.ItemTemplate> 

Tôi hy vọng công trình này cho một người khác. Tôi có một bối cảnh dữ liệu được thiết lập tự động để các ItemsControls, và bối cảnh dữ liệu này có hai thuộc tính: MyItems-đó là một bộ sưu tập, và một lệnh 'CustomCommand'. Do số ItemTemplate đang sử dụng một số DataTemplate, nên không thể truy cập trực tiếp vào số DataContext cấp trên. Sau đó, cách giải quyết để nhận DC của cha/mẹ là sử dụng đường dẫn tương đối và lọc theo loại ItemsControl.

14

RelativeSource so vớiElementName

Hai cách tiếp cận có thể đạt được kết quả tương tự,

RelativeSrouce

Binding="{Binding Path=DataContext.MyBindingProperty, 
      RelativeSource={RelativeSource AncestorType={x:Type Window}}}" 

Phương pháp này sẽ cho một điều khiển của một loại cửa sổ (trong ví dụ này) trong hình ảnh cây và khi nó tìm thấy nó về cơ bản bạn có thể truy cập nó là DataContext bằng cách sử dụng Path=DataContext..... Ưu điểm của phương pháp này là bạn không cần phải gắn với một cái tên và nó khá năng động, tuy nhiên, những thay đổi được thực hiện cho cây thị giác của bạn có thể ảnh hưởng đến phương pháp này và có thể phá vỡ nó.

ElementName

Binding="{Binding Path=DataContext.MyBindingProperty, ElementName=MyMainWindow} 

Phương pháp này referes đến một chất rắn tĩnh Name như vậy miễn là phạm vi của bạn có thể nhìn thấy nó, bạn fine.You nên gắn bó với quy ước đặt tên của bạn không vi phạm phương pháp này của khóa học. Cách tiếp cận là qute đơn giản và tất cả những gì bạn cần là chỉ định một Name="..." cho Window/UserControl của bạn.

Mặc dù cả ba loại (RelativeSource, Source, ElementName) đều có khả năng thực hiện tương tự, nhưng theo bài viết MSDN sau, mỗi bài viết tốt hơn được sử dụng trong lĩnh vực chuyên môn của riêng họ.

How to: Specify the Binding Source

Tìm mô tả ngắn gọn của từng cộng với một liên kết đến một chi tiết một trong bảng trên dưới cùng của trang.

Các vấn đề liên quan