Các đối tượng ràng buộc được xác định trong mã phía sau


88

Tôi có một số đối tượng được khởi tạo bằng mã phía sau, ví dụ: XAML được gọi là window.xaml và trong window.xaml.cs

protected Dictionary<string, myClass> myDictionary;

Làm cách nào để tôi có thể liên kết đối tượng này với, ví dụ, một dạng xem danh sách, chỉ sử dụng các đánh dấu XAML?

Cập nhật:

(Đây chính xác là tôi có trong mã thử nghiệm của mình):

<Window x:Class="QuizBee.Host.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="{Binding windowname}" Height="300" Width="300"
    DataContext="{Binding RelativeSource={RelativeSource Self}}">
    <Grid>
    </Grid>
</Window>

Và trong codebehind

public partial class Window1 : Window
{
    public const string windowname = "ABCDEFG";

    public Window1()
    {
        InitializeComponent();
    }
}

Giả sử tiêu đề phải trở thành "ABCDEFG" phải không? nhưng nó không hiển thị gì.


1
Thật kỳ lạ, Nếu tôi thay đổi thứ tự gán thuộc tính của cửa sổ, Nó không hoạt động. Nếu tôi đặt Thuộc tính "Title" theo sau là Thuộc tính "DataContext", ràng buộc sẽ không xảy ra. Bất cứ ai có thể giải thích điều này? <Window x: Class = "INotifyPropertyTest.MainWindow" xmlns = " schemas.microsoft.com/winfx/2006/xaml/presentation " xmlns: x = " schemas.microsoft.com/winfx/2006/xaml " xmlns: local = " clr-namespace: INotifyPropertyTest "Height =" 350 "Width =" 525 "DataContext =" {Binding RelativeSource = {RelativeSource self}} "Title =" {Binding WindowName} ">
Ramesh

Câu trả lời:


109

Bạn có thể đặt DataContext cho điều khiển, biểu mẫu, v.v. của mình như sau:

DataContext="{Binding RelativeSource={RelativeSource Self}}"

Làm rõ :

Bối cảnh dữ liệu được đặt thành giá trị ở trên nên được thực hiện ở bất kỳ phần tử nào "sở hữu" mã phía sau - vì vậy đối với một Cửa sổ, bạn nên đặt nó trong khai báo Cửa sổ.

Tôi có ví dụ của bạn làm việc với mã này:

<Window x:Class="MyClass"
  Title="{Binding windowname}"
  DataContext="{Binding RelativeSource={RelativeSource Self}}"
  Height="470" Width="626">

Tập DataContext ở cấp độ này sau đó được kế thừa bởi bất kỳ phần tử nào trong cửa sổ (trừ khi bạn thay đổi rõ ràng nó cho một phần tử con), vì vậy sau khi đặt DataContext cho Window, bạn sẽ có thể thực hiện liên kết thẳng với các thuộc tính CodeBehind từ bất kỳ điều khiển nào. trên cửa sổ.


1
"Tự" ở đây có nghĩa là kiểm soát, chứ không phải là toàn bộ lớp cửa sổ, phải không?
xandy

Thật kỳ lạ, Sau đây là mã tôi có và nó không hoạt động như mong đợi: public part class Window1: Window {public const string windowname = "ABCDEFG"; public Window1 () {InitializeComponent (); }} <Window x: Class = "QuizBee.Host.Window1" xmlns = " schemas.microsoft.com/winfx/2006/xaml/presentation " xmlns: x = " schemas.microsoft.com/winfx/2006/xaml " Tiêu đề = "{Binding windowname}" Height = "300" Width = "300" DataContext = "{Binding RelativeSource = {RelativeSource Self}}"> </Window>
xandy

9
Ồ, bây giờ ổn rồi, tôi đã đổi tên cửa sổ thành thuộc tính thay vì biến công khai thuần túy và nó có thể hiển thị ngay bây giờ! cảm ơn!
xandy

1
Tôi không thể tưởng tượng tại sao điều này không chỉ được đặt theo mặc định.
Okonomiyaki3000

122

Có một cách dễ dàng hơn để làm điều này. Bạn có thể gán Tên cho Window hoặc UserControl của mình, sau đó ràng buộc bởi ElementName.

Window1.xaml

<Window x:Class="QuizBee.Host.Window1"
        x:Name="Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <ListView ItemsSource="{Binding ElementName=Window1, Path=myDictionary}" />
</Window>

Window1.xaml.cs

public partial class Window1:Window
{
    // the property must be public, and it must have a getter & setter
    public Dictionary<string, myClass> myDictionary { get; set; }

    public Window1()
    {
        // define the dictionary items in the constructor
        // do the defining BEFORE the InitializeComponent();

        myDictionary = new Dictionary<string, myClass>()
        {
            {"item 1", new myClass(1)},
            {"item 2", new myClass(2)},
            {"item 3", new myClass(3)},
            {"item 4", new myClass(4)},
            {"item 5", new myClass(5)},
        }; 

        InitializeComponent();
    }
}

3
Tôi đã phải thay đổi x: Name (lỗi trình biên dịch CS0542). Sau đó, ElementName cần được thay đổi cho phù hợp.
Jack Miller

25

Mặc dù câu trả lời của Guy là đúng (và có thể phù hợp với 9 trong số 10 trường hợp), cần lưu ý rằng nếu bạn đang cố gắng thực hiện điều này từ một điều khiển đã có DataContext của nó được thiết lập thêm trong ngăn xếp, bạn sẽ đặt lại điều này khi bạn đặt DataContext trở lại chính nó:

DataContext="{Binding RelativeSource={RelativeSource Self}}"

Điều này tất nhiên sau đó sẽ phá vỡ các ràng buộc hiện có của bạn.

Nếu đúng như vậy, bạn nên đặt RelativeSource trên điều khiển mà bạn đang cố gắng ràng buộc, thay vì cha mẹ của nó.

tức là để liên kết với các thuộc tính của UserControl:

Binding Path=PropertyName, 
        RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}

Với mức độ khó hiện tại để xem điều gì đang xảy ra với liên kết dữ liệu, bạn nên ghi nhớ điều này ngay cả khi bạn thấy rằng cài đặt RelativeSource={RelativeSource Self}hiện đang hoạt động :)


1
Silverlight 4 không hỗ trợ FindAncestor. Tuy nhiên, bạn cần làm theo cách này, bạn có thể triển khai FindAncestor như được mô tả trên trang web này. http://blog.thekieners.com/2010/09/08/relativeource-binding-with-findancestor-mode-in-silverlight/
ShawnFeatherly

7

Chỉ cần giải thích rõ hơn một chút: Thuộc tính không có 'get', 'set' sẽ không thể bị ràng buộc

Tôi đang đối mặt với trường hợp giống như trường hợp của người hỏi. Và tôi phải có những điều sau để ràng buộc hoạt động bình thường:

//(1) Declare a property with 'get','set' in code behind
public partial class my_class:Window {
  public String My_Property { get; set; }
  ...

//(2) Initialise the property in constructor of code behind
public partial class my_class:Window {
  ...
  public my_class() {
     My_Property = "my-string-value";
     InitializeComponent();
  }

//(3) Set data context in window xaml and specify a binding
<Window ...
DataContext="{Binding RelativeSource={RelativeSource Self}}">
  <TextBlock Text="{Binding My_Property}"/>
</Window>

9
Chính xác thì làm thế nào bạn có thể có một thuộc tính mà không có 'get' và 'set'? Đó sẽ không phải là một lĩnh vực và không phải là một tài sản?
kjbartel

1

Xác định bộ chuyển đổi:

public class RowIndexConverter : IValueConverter
{
    public object Convert( object value, Type targetType,
                           object parameter, CultureInfo culture )
    {
        var row = (IDictionary<string, object>) value;
        var key = (string) parameter;
        return row.Keys.Contains( key ) ? row[ key ] : null;
    }

    public object ConvertBack( object value, Type targetType,
                               object parameter, CultureInfo culture )
    {
        throw new NotImplementedException( );
    }
}

Liên kết với định nghĩa tùy chỉnh của Từ điển. Có rất nhiều ghi đè mà tôi đã bỏ qua, nhưng trình chỉ mục là thứ quan trọng, vì nó tạo ra sự kiện thay đổi thuộc tính khi giá trị được thay đổi. Điều này là bắt buộc đối với ràng buộc nguồn đến đích.

public class BindableRow : INotifyPropertyChanged, IDictionary<string, object>
{
    private Dictionary<string, object> _data = new Dictionary<string, object>( );

    public object Dummy   // Provides a dummy property for the column to bind to
    {
        get
        {
            return this;
        }
        set
        {
            var o = value;
        }
    }


    public object this[ string index ]
    {
        get
        {
            return _data[ index ];
        }
        set
        {
            _data[ index ] = value;
            InvokePropertyChanged( new PropertyChangedEventArgs( "Dummy" ) ); // Trigger update
        }
    }


}

Trong tệp .xaml của bạn, hãy sử dụng trình chuyển đổi này. Tham khảo đầu tiên về nó:

<UserControl.Resources>
    <ViewModelHelpers:RowIndexConverter x:Key="RowIndexConverter"/>
</UserControl.Resources>

Sau đó, ví dụ: nếu từ điển của bạn có mục nhập khóa là "Tên", thì để liên kết với nó: hãy sử dụng

<TextBlock  Text="{Binding Dummy, Converter={StaticResource RowIndexConverter}, ConverterParameter=Name}">

1

Đặt thuộc tính "windowname" của bạn thành DependencyProperty và giữ nguyên giá trị còn lại.


0

Trong mã của bạn phía sau, hãy đặt DataContext của cửa sổ thành từ điển. Trong XAML của bạn, bạn có thể viết:

<ListView ItemsSource="{Binding}" />

Điều này sẽ liên kết ListView với từ điển.

Đối với các tình huống phức tạp hơn, đây sẽ là một tập hợp con của các kỹ thuật đằng sau mẫu MVVM .


0

Một cách là tạo một ObservableCollection (System.Collections.ObjectModel) và có dữ liệu từ điển của bạn trong đó. Sau đó, bạn sẽ có thể liên kết ObservableCollection với ListBox của mình.

Trong XAML của bạn, bạn nên có một cái gì đó như sau:

<ListBox ItemsSource="{Binding Path=Name_of_your_ObservableCollection" />

0

Tôi cũng gặp phải vấn đề này nhưng không phải vì tôi đang đặt biến cục bộ ... Tôi đang ở trong cửa sổ con và tôi cần đặt DataContext tương đối mà tôi vừa thêm vào Window XAML.

<Window x:Class="Log4Net_Viewer.LogItemWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    DataContext="{Binding RelativeSource={RelativeSource Self}}"
    Title="LogItemWindow" Height="397" Width="572">

0

Bạn có thể thử x: Tham khảo thủ thuật

<Window ... x:Name="myWindow"><ListBox ItemsSource="{Binding Items, Source={x:Reference myWindow}}" /></Window>
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.