2010-06-30 68 views
8

Tôi có một bộ sưu tập các đối tượng được lưu trữ trong CollectionViewSource và được liên kết với một số DataGrid. Tôi muốn hiển thị 'chế độ xem chi tiết' của đối tượng hiện được chọn trong DataGrid. Tôi có thể lấy đối tượng hiện tại bằng cách sử dụng CollectionViewSource.View.CurrentItem.WPF: Tự động ràng buộc danh sách (một số) thuộc tính của đối tượng

MyClass 
{ 
    [IsImportant] 
    AProperty{} 

    AnotherProperty{} 

    [IsImportant] 
    YetAnotherProperty{} 
} 

Những gì tôi muốn làm là hiển thị một nhãn (với tên tài sản) và một điều khiển (để chỉnh sửa) trong một hộp danh sách, cho mỗi người trong số những tài sản được đánh dấu bằng các thuộc tính IsImportant. Các ràng buộc phải làm việc giữa các chỉnh sửa được thực hiện, các DataGrid và đối tượng ủng hộ. Điều khiển được hiển thị sẽ thay đổi dựa trên loại thuộc tính, có thể là boolean, string hoặc IEnumerable<string> (Tôi đã viết IValueConverter để chuyển đổi giữa chuỗi có thể đếm được và dòng được phân tách bằng dấu gạch chéo).

Có ai biết phương pháp để thực hiện việc này không? Tôi hiện có thể hiển thị các giá trị của mỗi tài sản thông qua những điều sau đây, nhưng việc chỉnh sửa chúng sẽ không cập nhật các đối tượng ủng hộ:

listBox.ItemsSource = from p in typeof(MyClass).GetProperties() 
         where p.IsDefined(typeof(IsImportant), false) 
         select p.GetValue(_collectionViewSource.View.CurrentItem, null); 

Để làm rõ, Tôi muốn điều này xảy ra 'automagically', mà không tự xác định tên thuộc tính trong XAML. Nếu tôi có thể tự động thêm vào XAML trong thời gian chạy dựa trên các thuộc tính nào được đánh dấu bằng thuộc tính, điều đó cũng sẽ ổn.

Trả lời

12

Bạn muốn có một điều khiển mà có một nhãn với tên thuộc tính và kiểm soát để chỉnh sửa giá trị tài sản, vì vậy bắt đầu bằng cách tạo ra một lớp bọc lấy một tài sản của một đối tượng cụ thể để hoạt động như DataContext để kiểm soát rằng:

public class PropertyValue 
{ 
    private PropertyInfo propertyInfo; 
    private object baseObject; 

    public PropertyValue(PropertyInfo propertyInfo, object baseObject) 
    { 
     this.propertyInfo = propertyInfo; 
     this.baseObject = baseObject; 
    } 

    public string Name { get { return propertyInfo.Name; } } 

    public Type PropertyType { get { return propertyInfo.PropertyType; } } 

    public object Value 
    { 
     get { return propertyInfo.GetValue(baseObject, null); } 
     set { propertyInfo.SetValue(baseObject, value, null); } 
    } 
} 

Bạn muốn ràng buộc ItemsSource của một ListBox để một đối tượng để cư nó với các điều khiển, do đó tạo ra một IValueConverter rằng sẽ chuyển đổi một đối tượng vào một danh sách các đối tượng PropertyValue cho thuộc tính quan trọng của nó:

public class PropertyValueConverter 
    : IValueConverter 
{ 
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 
    { 
     return 
      from p in value.GetType().GetProperties() 
      where p.IsDefined(typeof(IsImportant), false) 
      select new PropertyValue(p, value); 
    } 

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 
    { 
     return Binding.DoNothing; 
    } 
} 

Bí quyết cuối cùng là bạn muốn điều khiển chỉnh sửa thay đổi dựa trên loại thuộc tính. Bạn có thể làm điều đó bằng cách sử dụng một ContentControl và thiết lập ContentTemplate thành một trong các mẫu trình soạn thảo khác nhau dựa trên kiểu thuộc tính. Ví dụ này sử dụng một CheckBox nếu tài sản là một Boolean và một TextBox khác:

<DataTemplate x:Key="CheckBoxTemplate"> 
    <CheckBox IsChecked="{Binding Value}"/> 
</DataTemplate> 
<DataTemplate x:Key="TextBoxTemplate"> 
    <TextBox Text="{Binding Value}"/> 
</DataTemplate> 
<Style x:Key="EditControlStyle" TargetType="ContentControl"> 
    <Setter Property="ContentTemplate" Value="{StaticResource TextBoxTemplate}"/> 
    <Style.Triggers> 
     <DataTrigger Binding="{Binding PropertyType}" Value="{x:Type sys:Boolean}"> 
      <Setter Property="ContentTemplate" Value="{StaticResource CheckBoxTemplate}"/> 
     </DataTrigger> 
    </Style.Triggers> 
</Style> 
<DataTemplate DataType="{x:Type local:PropertyValue}"> 
    <StackPanel Orientation="Horizontal"> 
     <Label Content="{Binding Name}"/> 
     <ContentControl Style="{StaticResource EditControlStyle}" Content="{Binding}"/> 
    </StackPanel> 
</DataTemplate> 

Sau đó, bạn chỉ có thể tạo ListBox của bạn như:

<ItemsControl ItemsSource="{Binding Converter={StaticResource PropertyValueConverter}}"/> 
+0

Điều đó có vẻ tuyệt vời; Tôi sẽ thực hiện nó ngay sau khi tôi có một cơ hội và cho bạn biết làm thế nào nó đi. Cảm ơn! –

+0

Điều này hoạt động chủ yếu là tuyệt vời. Tôi chỉ cần thêm một kiểm tra null vào PropertyValueConverter, vì nó đã nhận được một đối tượng null khi ràng buộc được đặt đầu tiên. Vấn đề duy nhất là hiện tại, nếu tôi thay đổi một giá trị trên điều khiển này, nó sẽ không được truyền qua datagrid. Có lẽ là đối tượng sao lưu đang nhận được thay đổi, vì giá trị thay đổi vẫn được hiển thị bởi các itemscontrol, nhưng lưới điện không được thông báo. Đây có phải là để làm với Binding.DoNothing? –

+1

@Daniel: Đối tượng của bạn sẽ cần triển khai INotifyPropertyChanged nếu bạn muốn các bản cập nhật được thực hiện bởi một điều khiển được phản ánh trong các điều khiển khác được liên kết với cùng một thuộc tính. Bạn cũng sẽ cần triển khai INotifyPropertyChanged trên PropertyValue. PropertyValueConverter là bản thân ràng buộc ItemsSource, là một chiều, vì vậy ConvertBack sẽ không được gọi và Binding.DoNothing sẽ không thành vấn đề. – Quartermeister

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