在代码后台定义绑定对象

94

我有一些在代码后端实例化的对象,例如,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>

在代码后台

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

    public Window1()
    {
        InitializeComponent();
    }
}

假设标题应该变成 "ABCDEFG",但最终没有显示任何内容。


1
有趣的是,如果我更改窗口属性分配的顺序,它就无法工作。如果我先设置“Title”属性,然后是“DataContext”属性,绑定就不会发生。有人能解释一下吗?<Window x:Class="INotifyPropertyTest.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local ="clr-namespace:INotifyPropertyTest" Height="350" Width="525" DataContext="{Binding RelativeSource={RelativeSource self}}" Title="{Binding WindowName}" > - Ramesh
11个回答

132

有一种更简单的方法可以实现这个。你可以给你的窗口或用户控件指定一个名称,然后通过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();
    }
}

4
我不得不更改x:Name(编译器错误CS0542)。然后相应地需要更改ElementName。 - Jack Miller

122

您可以像这样为控件、表单等设置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
这里的“Self”指的是控件,而不是整个窗口类,对吗? - xandy
很奇怪,以下是我的代码,但它并没有按预期工作: public partial class Window1 : Window { public const string windowname = "ABCDEFG"; public Window1() { InitializeComponent(); } }<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}}"> </Window> - xandy
11
好的,现在没问题了。我把窗口名称改成属性而不是纯公共变量,它现在可以显示了!谢谢! - xandy
2
我无法想象为什么这不是默认设置。 - Okonomiyaki3000

31

虽然 Guy 的答案是正确的(并且可能适用于10个案例中的9个),但值得注意的是,如果你正在尝试从已经设置了 DataContext 的控件中执行此操作,则在将 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

8

稍微解释一下:如果一个属性没有“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>

12
如果没有“get”和“set”,那么如何拥有一个属性呢?这不会变成一个字段而不是属性吗? - kjbartel

1
一种方法是创建一个ObservableCollection(System.Collections.ObjectModel),并将您的字典数据放在其中。然后,您应该能够将ObservableCollection绑定到您的ListBox。
在您的XAML中,您应该有类似以下的内容:
<ListBox ItemsSource="{Binding Path=Name_of_your_ObservableCollection" />

1
在您的代码后台中,将窗口的DataContext设置为字典。在您的XAML中,您可以编写:
<ListView ItemsSource="{Binding}" />

这将把 ListView 绑定到字典。

对于更复杂的情况,这将是MVVM模式背后技术的子集。


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

定义一个转换器:

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>

例如,如果您的字典中有一个键为“Name”的条目,则要绑定它:使用

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

0
将您的属性“windowname”设置为DependencyProperty,其余保持不变。

0
你可以尝试使用 x:Reference 技巧。
<Window ... x:Name="myWindow"><ListBox ItemsSource="{Binding Items, Source={x:Reference myWindow}}" /></Window>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接