การผูกวัตถุที่กำหนดไว้ในรหัสหลัง


87

ฉันมีวัตถุบางอย่างที่สร้างอินสแตนซ์ในโค้ดข้างหลังตัวอย่างเช่น XAML เรียกว่า window.xaml และภายใน window.xaml.cs

protected Dictionary<string, myClass> myDictionary;

ฉันจะผูกออบเจ็กต์นี้เข้ากับมุมมองรายการโดยใช้เฉพาะมาร์กอัป XAML ได้อย่างไร

อัปเดต:

(นี่คือรหัสทดสอบที่ฉันมี):

<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>

และใน codebehind

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

    public Window1()
    {
        InitializeComponent();
    }
}

สมมติว่าชื่อเรื่องควรจะกลายเป็น "ABCDEFG" ใช่ไหม แต่จบลงด้วยการไม่แสดงอะไรเลย


1
น่าแปลกที่ถ้าฉันเปลี่ยนลำดับของการกำหนดคุณสมบัติของหน้าต่างมันไม่ทำงาน ถ้าฉันตั้งค่าคุณสมบัติ "Title" ตามด้วยคุณสมบัติ "DataContext" การผูกจะไม่เกิดขึ้น ใครช่วยอธิบายเรื่องนี้ <หน้าต่าง 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

คำตอบ:


109

คุณสามารถตั้งค่า DataContext สำหรับคอนโทรลฟอร์มและอื่น ๆ ได้ดังนี้:

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

ชี้แจง :

บริบทข้อมูลที่ตั้งค่าเป็นค่าด้านบนควรทำที่องค์ประกอบใดก็ตามที่ "เป็นเจ้าของ" โค้ดที่อยู่เบื้องหลัง - ดังนั้นสำหรับหน้าต่างคุณควรตั้งค่าในการประกาศหน้าต่าง

ฉันมีตัวอย่างของคุณที่ใช้รหัสนี้:

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

DataContext ที่ตั้งค่าในระดับนี้จะได้รับการสืบทอดโดยองค์ประกอบใด ๆ ในหน้าต่าง (เว้นแต่คุณจะเปลี่ยนเป็นองค์ประกอบลูกอย่างชัดเจน) ดังนั้นหลังจากตั้งค่า DataContext สำหรับหน้าต่างแล้วคุณควรจะสามารถเชื่อมโยงโดยตรงกับคุณสมบัติ CodeBehind จากตัวควบคุมใด ๆ บนหน้าต่าง


1
"ตัวเอง" ในที่นี้หมายถึงการควบคุมแทนที่จะเป็นคลาสหน้าต่างทั้งหมดใช่หรือไม่
xandy

แปลกพอต่อไปนี้เป็นรหัสที่ฉันมีและมันไม่ทำงานตามที่คาดไว้: คลาสบางส่วนสาธารณะ Window1: Window {สตริงสาธารณะ const windowname = "ABCDEFG"; Window1 สาธารณะ () {InitializeComponent (); }} <Window x: Class = "QuizBee.Host.Window1" xmlns = " schemas.microsoft.com/winfx/2006/xaml/presentation " xmlns: x = " schemas.microsoft.com/winfx/2006/xaml " Title = "{Binding windowname}" Height = "300" Width = "300" DataContext = "{Binding RelativeSource = {RelativeSource Self}}"> </Window>
xandy

10
โอเคตอนนี้ฉันเปลี่ยน windowname เป็นทรัพย์สินแทนที่จะเป็นตัวแปรสาธารณะบริสุทธิ์และสามารถแสดงได้แล้ว! ขอบคุณ!
xandy

1
ฉันนึกไม่ออกว่าทำไมถึงไม่ตั้งค่าตามค่าเริ่มต้น
Okonomiyaki3000

122

มีวิธีที่ง่ายกว่ามากในการทำเช่นนี้ คุณสามารถกำหนดชื่อให้กับหน้าต่างหรือ UserControl ของคุณจากนั้นเชื่อมโยงโดย 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
ฉันต้องเปลี่ยน x: ชื่อ (ข้อผิดพลาดของคอมไพเลอร์ CS0542) จากนั้น ElementName จะต้องเปลี่ยนตาม
Jack Miller

25

ในขณะที่คำตอบของ Guy ถูกต้อง (และอาจเหมาะกับ 9 ใน 10 กรณี) แต่ก็น่าสังเกตว่าหากคุณพยายามทำสิ่งนี้จากตัวควบคุมที่มี DataContext ตั้งค่าสแต็กเพิ่มเติมอยู่แล้วคุณจะรีเซ็ตสิ่งนี้เมื่อคุณตั้งค่า DataContext กลับสู่ตัวเอง:

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

แน่นอนว่าสิ่งนี้จะทำลายการผูกมัดที่มีอยู่ของคุณ

ในกรณีนี้คุณควรตั้งค่า RelativeSource บนตัวควบคุมที่คุณพยายามผูกแทนที่จะเป็นพาเรนต์

เช่นสำหรับการผูกกับคุณสมบัติของ UserControl:

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

เมื่อพิจารณาถึงความยากลำบากในการดูว่าเกิดอะไรขึ้นกับการผูกข้อมูลในขณะนี้คุณควรคำนึงถึงสิ่งนี้แม้ว่าคุณจะพบว่าการตั้งค่านั้นใช้RelativeSource={RelativeSource Self}งานได้ในขณะนี้ :)


1
Silverlight 4 ไม่รองรับ FindAncestor อย่างไรก็ตามคุณต้องทำด้วยวิธีนี้คุณสามารถใช้ FindAncestor ตามที่อธิบายไว้ในไซต์นี้ http://blog.thekieners.com/2010/09/08/relativesource-binding-with-findancestor-mode-in-silverlight/
ShawnFeatherly

7

คำชี้แจงเพิ่มเติมเล็กน้อย: คุณสมบัติที่ไม่มี 'get', 'set' จะไม่สามารถผูกมัดได้

ฉันกำลังเผชิญกับคดีนี้เหมือนกับกรณีของผู้ถาม และฉันต้องมีสิ่งต่อไปนี้เพื่อให้การผูกทำงานได้อย่างถูกต้อง:

//(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>

10
คุณจะมีทรัพย์สินโดยไม่มี 'get' และ 'set' ได้อย่างไร? นั่นจะไม่ใช่ทุ่งนาไม่ใช่ทรัพย์สินหรือ?
kjbartel

0

ในโค้ดของคุณด้านหลังตั้งค่า DataContext ของหน้าต่างเป็นพจนานุกรม ใน XAML ของคุณคุณสามารถเขียน:

<ListView ItemsSource="{Binding}" />

สิ่งนี้จะผูก ListView กับพจนานุกรม

สำหรับสถานการณ์ที่ซับซ้อนมากขึ้นนี่จะเป็นเทคนิคย่อยที่อยู่เบื้องหลังรูปแบบMVVM


0

วิธีหนึ่งก็คือการสร้าง ObservableCollection (System.Collections.ObjectModel) และมีข้อมูลพจนานุกรมของคุณอยู่ในนั้น จากนั้นคุณจะสามารถผูก ObservableCollection กับ ListBox ของคุณได้

ใน XAML ของคุณคุณควรมีสิ่งนี้:

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

0

กำหนดตัวแปลง:

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( );
    }
}

ผูกกับคำจำกัดความที่กำหนดเองของพจนานุกรม มีการลบล้างจำนวนมากที่ฉันข้ามไป แต่ตัวสร้างดัชนีเป็นสิ่งที่สำคัญเนื่องจากจะปล่อยเหตุการณ์ที่เปลี่ยนแปลงคุณสมบัติเมื่อค่ามีการเปลี่ยนแปลง สิ่งนี้จำเป็นสำหรับการเชื่อมโยงต้นทางกับเป้าหมาย

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
        }
    }


}

ในไฟล์. xaml ของคุณให้ใช้ตัวแปลงนี้ อ้างอิงครั้งแรก:

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

จากนั้นตัวอย่างเช่นหากพจนานุกรมของคุณมีรายการที่มีคีย์คือ "ชื่อ" ให้ผูกกับมัน: ใช้

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


0

ฉันมีปัญหาเดียวกันนี้ แต่ของฉันไม่ใช่เพราะฉันกำลังตั้งค่าตัวแปรในเครื่อง ... ฉันอยู่ในหน้าต่างลูกและฉันต้องตั้งค่า DataContext แบบสัมพัทธ์ซึ่งฉันเพิ่งเพิ่มลงใน 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

นั่นคือวิธีของฉันในการผูกกับรหัสด้านหลัง (ดูคุณสมบัติDataTemplateSelector)

public partial class MainWindow : Window
{
  public MainWindow()
  {
    this.DataTemplateSelector = new MyDataTemplateSelector();

    InitializeComponent();

    // ... more initializations ...
  }

  public DataTemplateSelector DataTemplateSelector { get; }

  // ... more code stuff ...
}

ใน XAML จะอ้างอิงโดยRelativeSourceผ่าน Ancestors ขึ้นไปที่มีWindowดังนั้นฉันจึงอยู่ที่Windowชั้นเรียนของฉันและใช้คุณสมบัติผ่านPathการประกาศ:

<GridViewColumn Header="Value(s)"
                CellTemplateSelector="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataTemplateSelector}"/>

การตั้งค่าของทรัพย์สินDataTemplateSelectorก่อนที่จะเรียกInitializeComponentขึ้นอยู่กับการดำเนินงานของที่หายไปIPropertyChangedหรือการใช้งานของการดำเนินงานที่มีเพื่อให้การสื่อสารไม่ทำงานเกี่ยวกับการเปลี่ยนแปลงของทรัพย์สินDependencyPropertyDataTemplateSelector

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.