2013-05-04 21 views
21

Tôi có flipview này:Làm cách nào để truy cập một điều khiển bên trong một DataTemplate XAML?

<FlipView x:Name="models_list" SelectionChanged="selectionChanged"> 
<FlipView.ItemTemplate> 
      <DataTemplate> 
       <Grid x:Name="cv"> 
         <Image x:Name="img1" Source = "{Binding ModelImage}" Stretch="Fill" Tag="{Binding ModelTag}"/> 
       </Grid> 
      </DataTemplate> 
    </FlipView.ItemTemplate> 

Tôi muốn tìm img1 của chỉ số đang được chọn. Trong khi tìm kiếm cho nó tôi thấy phương pháp này trên một số bài ở đây:

private DependencyObject FindChildControl<T>(DependencyObject control, string ctrlName) 
    { 
     int childNumber = VisualTreeHelper.GetChildrenCount(control); 
     for (int i = 0; i < childNumber; i++) 
     { 
      DependencyObject child = VisualTreeHelper.GetChild(control, i); 
      FrameworkElement fe = child as FrameworkElement; 
      // Not a framework element or is null 
      if (fe == null) return null; 

      if (child is T && fe.Name== ctrlName) 
      { 
       // Found the control so return 
       return child; 
      } 
      else 
      { 
       // Not found it - search children 
       DependencyObject nextLevel = FindChildControl<T>(child, ctrlName); 
       if (nextLevel != null) 
        return nextLevel; 
      } 
     } 
     return null; 
    } 

Nó trả về cho tôi những hình ảnh trên chỉ số đầu tiên của flipview nhưng tôi cần cái hiện hữu trên các chỉ số hiện đang được chọn .. Tôi cố gắng để chỉnh sửa phương pháp này nhưng tôi không thể tìm thấy sự kiểm soát cần thiết. Ai giúp tôi với?

+1

Đó thường là cách tiếp cận sai. Cụ thể là bạn muốn làm gì với hình ảnh đó? Lưu ý rằng FlipView ảo hóa các con của nó để Hình ảnh có thể không tồn tại. –

+0

@FilipSkakun Tôi muốn nhận tọa độ màn hình của hình ảnh đó. Tôi có thể làm điều đó theo bất kỳ cách nào khác không? – Tehreem

+0

Bạn cần tọa độ màn hình để làm gì? –

Trả lời

49

Sự cố bạn gặp phải là DataTemplate được lặp lại và nội dung đang được tạo bởi FlipView. Các Name không được tiếp xúc bởi vì nó sẽ xung đột với anh chị em trước đó đã được tạo ra (hoặc người tiếp theo sẽ được).

Vì vậy, để có được phần tử được đặt tên trong số DataTemplate, trước tiên bạn phải nhận mục được tạo và sau đó tìm kiếm bên trong mục được tạo cho phần tử bạn muốn. Hãy nhớ rằng, cây logic trong XAML là cách bạn truy cập mọi thứ theo tên. Các mục đã tạo không có trong cây logic. Thay vào đó, chúng nằm trong Visual Tree (tất cả các điều khiển đều nằm trong Visual Tree). Điều đó có nghĩa là nó nằm trong Visual Tree, bạn phải tìm kiếm điều khiển mà bạn muốn tham chiếu. VisualTreeHelper cho phép bạn thực hiện việc này.

Bây giờ, làm thế nào để làm điều đó?

tôi đã viết một bài báo về vấn đề này bởi vì nó là một câu hỏi lặp đi lặp lại như vậy: http://blog.jerrynixon.com/2012/09/how-to-access-named-control-inside-xaml.html nhưng thịt của giải pháp là một phương pháp đệ quy mà trông giống như sau:

public void TestFirstName() 
{ 
    foreach (var item in MyFlipView.Items) 
    { 
     var _Container = MyFlipView.ItemContainerGenerator 
      .ContainerFromItem(item); 
     var _Children = AllChildren(_Container); 

     var _FirstName = _Children 
      // only interested in TextBoxes 
      .OfType<TextBox>() 
      // only interested in FirstName 
      .First(x => x.Name.Equals("FirstName")); 

     // test & set color 
     _FirstName.Background = 
      (string.IsNullOrWhiteSpace(_FirstName.Text)) 
      ? new SolidColorBrush(Colors.Red) 
      : new SolidColorBrush(Colors.White); 
    } 
} 

public List<Control> AllChildren(DependencyObject parent) 
{ 
    var _List = new List<Control>(); 
    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++) 
    { 
     var _Child = VisualTreeHelper.GetChild(parent, i); 
     if (_Child is Control) 
      _List.Add(_Child as Control); 
     _List.AddRange(AllChildren(_Child)); 
    } 
    return _List; 
} 

Vấn đề mấu chốt ở đây là một phương pháp như thế này được tất cả các trẻ em, và sau đó trong danh sách kết quả của điều khiển con bạn có thể tìm kiếm các điều khiển cụ thể mà bạn muốn. Có lý?

Và bây giờ để trả lời câu hỏi của bạn!

Bởi vì bạn đặc biệt muốn mục đang được chọn, bạn chỉ có thể cập nhật mã như thế này:

if (MyFlipView.SelectedItem == null) 
    return; 
var _Container = MyFlipView.ItemContainerGenerator 
    .ContainerFromItem(MyFlipView.SelectedItem); 
// then the same as above... 
+0

Jerry - Dường như điều này chỉ hoạt động nếu bạn có một SelectedItem. Làm thế nào bạn sẽ nhắm mục tiêu một yếu tố thông qua một nút bấm bên ngoài giới hạn của các mặt hàng templated? Xem http://stackoverflow.com/questions/23645331/how-to-accessing-elements-in-xaml-datatemplate-listview-without-interacting-with – Rob

+0

Không. Nó luôn hoạt động. –

+0

@ JerryNixon-MSFT Tôi đang cố gắng sử dụng mã của bạn trong một cửa sổ điện thoại 8.1 ứng dụng, nhưng nó không thể tìm thấy một tham chiếu đến VisualTreeHelperClass. Nhưng khi tôi tìm kiếm nó trong trình duyệt đối tượng, tôi có thể tìm thấy nó trong System.Windows.Media và Windows.UI.Xaml.Media. Ngoài ra 'OfType ()' cho lỗi Systems.Generic.Connections.List không chứa định nghĩa của OfType(). [Cấu trúc của FlipView] của tôi (http://pastebin.com/9ud7sCyv) – user3263192

2

Giải pháp đơn giản để truy cập vào các phần tử trong DataTemplate là bọc nội dung của DataTemplate trong UserControl, nơi bạn có quyền truy cập vào tất cả các phần tử giao diện người dùng trong mục ItemsControl. Tôi nghĩ FlipView thường ảo hóa các mục của nó vì vậy ngay cả khi bạn có 100 mục bị ràng buộc - chỉ 2-3 có thể có một biểu diễn hiện tại trong giao diện người dùng (1-2 trong số chúng bị ẩn), vì vậy bạn phải nhớ rằng khi bạn muốn thay thế bất kỳ thứ gì và chỉ thực sự thực hiện thay đổi khi một mục được tải vào điều khiển.

Nếu bạn thực sự cần phải xác định vùng chứa mục đại diện cho mặt hàng trong ItemsSource - bạn có thể kiểm tra thuộc tính của FlipView là ItemContainerGenerator và phương thức ContainerFromItem() của nó.

Để nhận tọa độ của một mục, bạn có thể sử dụng phương thức mở rộng GetBoundingRect() trong Bộ công cụ WinRT XAML.

Tuy nhiên, dựa trên nhận xét của bạn, có thể cách tiếp cận tốt nhất thực sự hoàn toàn khác. Nếu bạn đang ràng buộc FlipView của mình vào nguồn - bạn thường có thể kiểm soát hình ảnh được hiển thị hoặc phủ lên bằng cách thay đổi các thuộc tính của các mục thu thập nguồn bị ràng buộc.

+0

Tôi giải quyết vấn đề bằng cách lấy tất cả các hình ảnh trong FlipView trong một danh sách và sau đó tôi lặp qua danh sách đó để tìm hình ảnh yêu cầu của tôi. Có bạn là đúng Tôi chỉ có thể truy cập 3 mặt hàng của FlipView tại một thời điểm nhưng như tôi cần hiện đang tập trung mục, nó luôn luôn hiện diện trong ba mục. Cảm ơn bạn đã giúp đỡ của bạn – Tehreem

+0

@Tehreem bạn có thể cung cấp giải pháp tôi đang phải đối mặt với tình hình tương tự. –

+0

Bạn có thể duyệt qua cây trực quan bằng cách sử dụng 'VisualTreeHelper', nhưng tôi đã nghe nó có thể không phải là cách hiệu quả nhất để truy cập vào các phần tử, vì vậy việc gói' DataTemplate' của bạn trong 'UserControl' có thể hoạt động tốt hơn. Dù công trình là tốt mặc dù. –

0

Nếu bạn chỉ muốn màn hình tọa độ trên mục nhấp chuột ... Bạn có thể đăng ký một handler nhấp chuột trên hình ảnh và sau đó sử dụng senderimg.transform tovisual (null) điều này cung cấp cho bạn một generaltransforn mà từ đó bạn có thể nhận được tọa độ điểm hiện tại.

9

có lẽ một chút cách tiếp cận chung chung hơn có thể là một cái gì đó như thế này:

private List<Control> AllChildren(DependencyObject parent) 
{ 
    var _List = new List<Control>(); 
    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++) 
    { 
     var _Child = VisualTreeHelper.GetChild(parent, i); 
     if (_Child is Control) 
     { 
     _List.Add(_Child as Control); 
     } 
     _List.AddRange(AllChildren(_Child)); 
    } 
    return _List; 
} 


private T FindControl<T> (DependencyObject parentContainer, string controlName) 
{    
     var childControls = AllChildren(parentContainer); 
     var control = childControls.OfType<Control>().Where(x => x.Name.Equals(controlName)).Cast<T>().First();   
     return control; 
} 

Bạn sẽ gọi FindControl như thế này:

var parentContainer = this.flipView.ItemContainerGenerator.ContainerFromItem(this.flipView.SelectedItem); 
var myImage = FindControl<Image>(parentContainer, "img1"); 

//suppose you want to change the visibility 
myImage.Visibility = Windows.UI.Xaml.Visibility.Collapsed;   
0

tôi đã đấu tranh whit cả ngày hôm nay, và có vẻ như rằng kiểm soát đã được nạp (kết xuất) để có được điều khiển con của nó từ DataTemplate. Điều này có nghĩa rằng bạn không thể sử dụng mã này hoặc bất kỳ mã nào (cho cùng một mục đích) trên cửa sổ được nạp, được khởi tạo ... Bạn có thể sử dụng nó như thế nào sau khi điều khiển được khởi tạo ví dụ trên SelectedItem.

Tôi khuyên bạn nên sử dụng trình chuyển đổi và xác định hành động được yêu cầu trong trình chuyển đổi, thay vì truy cập điều khiển trực tiếp.

0

Vì vậy, nếu bạn giao Nguồn thông qua ràng buộc tại sao bạn không làm tương tự với phần còn lại: <FlipView SelectedIndex="{Binding SIndex}" SelectedItem="{Binding SItem}" SelectedValue="{Binding SValue}"/>

Trước tiên, bạn phải chuẩn bị diễn ra đối với tài sản này. Nó có thể là một lớp học có nguồn gốc từ INotifyPropertyChanged.

Thuê MVVM để làm việc cho Bạn, làm được nhiều nhất trong XAML. Tất nhiên lúc bắt đầu nó có vẻ là công việc nhiều hơn nhưng với dự án phức tạp hơn với MVVM sẽ được mạch lạc và linh hoạt.