Liên kết với một phương pháp trong WPF?


90

Làm cách nào để bạn liên kết với một phương thức đối tượng trong trường hợp này trong WPF?

public class RootObject
{
    public string Name { get; }

    public ObservableCollection<ChildObject> GetChildren() {...}
}

public class ChildObject
{
    public string Name { get; }
}

XAML:

<TreeView ItemsSource="some list of RootObjects">
    <TreeView.Resources>
        <HierarchicalDataTemplate DataType="{x:Type data:RootObject}" 
                                  ItemsSource="???">
            <TextBlock Text="{Binding Path=Name}" />
        </HierarchicalDataTemplate>
        <HierarchicalDataTemplate DataType="{x:Type data:ChildObject}">
            <TextBlock Text="{Binding Path=Name}" />
        </HierarchicalDataTemplate>
    </TreeView.Resources>
</TreeView>

Ở đây tôi muốn liên kết với GetChildrenphương thức trên mỗi RootObjectcây.

CHỈNH SỬA Việc liên kết với một ObjectDataProviderdường như không hoạt động vì tôi đang ràng buộc với một danh sách các mục và ObjectDataProvidercần một phương thức tĩnh hoặc nó tạo ra phiên bản riêng và sử dụng nó.

Ví dụ, sử dụng câu trả lời của Matt, tôi nhận được:

System.Windows.Data Error: 33: ObjectDataProvider không thể tạo đối tượng; Loại = 'RootObject'; Error = 'Các tham số sai cho hàm tạo.'

Lỗi System.Windows.Data: 34: ObjectDataProvider: Không thể gọi phương thức trên loại; Phương thức = 'GetChildren'; Loại = 'RootObject'; Error = 'Thành viên được chỉ định không thể được gọi vào target.' TargetException: 'System.Reflection.TargetException: Phương thức không tĩnh yêu cầu một mục tiêu.


Uh, đúng vậy. ObjectDataProvider có thuộc tính ObjectInstance (để gọi phương thức của nó trên một phiên bản cụ thể) nhưng tôi không nghĩ đó là thuộc tính phụ thuộc, vì vậy bạn không thể ràng buộc nó (AFAIK).
Matt Hamilton

1
Vâng, tôi đã cố gắng liên kết với ObjectInstance và phát hiện ra nó không phải là thuộc tính phụ thuộc.
Cameron MacFarland

Tôi sẽ để lại câu trả lời của tôi ở đó, vừa để cung cấp cho bạn cập nhật một số ngữ cảnh và để giúp người khác tìm thấy câu hỏi này với một vấn đề tương tự.
Matt Hamilton

Bạn có thực sự cần liên kết với ObjectInstance không? (Nó sẽ thay đổi) Giả sử, do đó bạn có thể tạo ra thay vì xử lý sự thay đổi-sự kiện riêng của bạn và cập nhật các ObjectDataProvider trong mã ...
Tim Lovell-Smith

1
Chỉ cần cập nhật câu trả lời của tôi với một số mã nguồn, một năm sau thực tế.
Drew Noakes

Câu trả lời:


71

Một cách tiếp cận khác có thể phù hợp với bạn là tạo một tùy chỉnh IValueConverterlấy tên phương thức làm tham số, để nó sẽ được sử dụng như sau:

ItemsSource="{Binding 
    Converter={StaticResource MethodToValueConverter},
    ConverterParameter='GetChildren'}"

Bộ chuyển đổi này sẽ tìm và gọi phương thức bằng phản xạ. Điều này yêu cầu phương thức không có bất kỳ đối số nào.

Dưới đây là một ví dụ về nguồn của bộ chuyển đổi như vậy:

public sealed class MethodToValueConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var methodName = parameter as string;
        if (value==null || methodName==null)
            return value;
        var methodInfo = value.GetType().GetMethod(methodName, new Type[0]);
        if (methodInfo==null)
            return value;
        return methodInfo.Invoke(value, new object[0]);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException("MethodToValueConverter can only be used for one way conversion.");
    }
}

Và một bài kiểm tra đơn vị tương ứng:

[Test]
public void Convert()
{
    var converter = new MethodToValueConverter();
    Assert.AreEqual("1234", converter.Convert(1234, typeof(string), "ToString", null));
    Assert.AreEqual("ABCD", converter.Convert(" ABCD ", typeof(string), "Trim", null));

    Assert.IsNull(converter.Convert(null, typeof(string), "ToString", null));

    Assert.AreEqual("Pineapple", converter.Convert("Pineapple", typeof(string), "InvalidMethodName", null));
}

Lưu ý rằng bộ chuyển đổi này không thực thi targetTypetham số.


6
Hmmm, ... có vẻ giống như một vụ hack nhưng tôi bắt đầu nghĩ rằng đây có thể là cách duy nhất. Nó chắc chắn sẽ là dễ dàng nhất!
EightyOne Unite

25

Không chắc nó sẽ hoạt động tốt như thế nào trong kịch bản của bạn, nhưng bạn có thể sử dụng thuộc MethodNametính trên ObjectDataProviderđể yêu cầu nó gọi một phương thức cụ thể (với các tham số cụ thể của thuộc tính của bạn MethodParameters) để truy xuất dữ liệu của nó.

Đây là đoạn trích được lấy trực tiếp từ trang MSDN:

<Window.Resources>
    <ObjectDataProvider ObjectType="{x:Type local:TemperatureScale}"
        MethodName="ConvertTemp" x:Key="convertTemp">
        <ObjectDataProvider.MethodParameters>
            <system:Double>0</system:Double>
            <local:TempType>Celsius</local:TempType>
        </ObjectDataProvider.MethodParameters>
    </ObjectDataProvider>
</Window.Resources>

Vì vậy, đó là cách ObjectDataProvidergọi một ConvertTempphương thức trên một thể hiện của một TemperatureScalelớp, truyền hai tham số ( 0TempType.Celsius).


10

Bạn có phải ràng buộc với phương pháp không?

Bạn có thể liên kết với một thuộc tính mà phương thức là getter?

public ObservableCollection<ChildObject> Children
{
   get
   {
      return GetChildren();
   }
}

2
Tôi coi nhận xét của Cameron có nghĩa là anh ta đang ràng buộc với một loại mà anh ta không thể thêm thuộc tính vào.
Drew Noakes

2
Bạn nên tránh ràng buộc với các thuộc tính gọi các phương thức đặc biệt nếu phương thức đó có thể hoạt động lâu dài. Có các phương thức như vậy, thuộc tính của nó không phải là thiết kế tốt vì người dùng mã mong đợi một thuộc tính chỉ truy cập vào một biến cục bộ.
markmnl

@markmnl vậy điểm ràng buộc trực tiếp với hàm là gì? Vậy câu hỏi của OP không có ý nghĩa gì đối với trường hợp của bạn?
Teoman shipahi 14/02/15

4

Trừ khi bạn có thể thêm một thuộc tính để gọi phương thức (hoặc tạo một lớp trình bao bọc để thêm thuộc tính đó), cách duy nhất tôi biết là sử dụng ValueConverter.


3

ObjectDataProvider cũng có thuộc tính ObjectInstance có thể được sử dụng thay cho ObjectType


3

Bạn có thể sử dụng System.ComponentModelđể xác định động các thuộc tính cho một loại (chúng không phải là một phần của siêu dữ liệu đã biên dịch). Tôi đã sử dụng phương pháp này trong WPF để cho phép liên kết với một kiểu lưu trữ các giá trị của nó trong các trường, vì không thể liên kết với các trường.

Các ICustomTypeDescriptorTypeDescriptionProvider loại và có thể cho phép bạn đạt được những gì bạn muốn. Theo bài báo này :

TypeDescriptionProvidercho phép bạn viết một lớp riêng biệt thực hiện ICustomTypeDescriptorvà sau đó đăng ký lớp này làm nhà cung cấp mô tả cho các kiểu khác.

Tôi chưa tự mình thử cách tiếp cận này, nhưng tôi hy vọng nó hữu ích trong trường hợp của bạn.


0

Để liên kết với phương thức của một đối tượng trong kịch bản WPF của bạn, bạn có thể liên kết với một thuộc tính trả về một đại biểu.

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.