7

我有一个 WPF 项目,其中有 4 个TextBlock. 我想要的是改变Text每个TextBlockvia Binding

到目前为止,我有我的 XAML:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <TextBlock x:Name="First" Text="{Binding FirstString}" Grid.Row="0"/>
    <TextBlock x:Name="Second" Text="{Binding SecondString}" Grid.Row="1"/>
    <TextBlock x:Name="Third" Text="{Binding ThirdString}" Grid.Row="2"/>
    <TextBlock x:Name="Fourth" Text="{Binding FourthString}" Grid.Row="3"/>
</Grid>

在我的代码中,我有:

public partial class MainWindow : Window
{
    public string FirstString { get; set; }
    public string SecondString { get; set; }
    public string ThirdString { get; set; }
    public string FourthString { get; set; }

    public MainWindow()
    {
        InitializeComponent();

        FirstString = "First";
        SecondString = "Second";
        ThirdString= "Third";
        FourthString= "Fourth";
    }
}

但这Binding根本不起作用。难道我做错了什么?

编辑:在评论中遵循 Chris Mantle 的建议查看调试输出(我必须为绑定启用警告)后,我收到以下错误:

System.Windows.Data 信息:10:无法使用绑定检索值,并且不存在有效的备用值;改用默认值。绑定表达式:路径=第一个字符串;数据项=空;目标元素是'TextBlock'(名称='First');目标属性是“文本”(类型“字符串”)

4

5 回答 5

20

有几件事是不正确的。标记将Binding查看DataContext控件属性中的对象。DataContext除非另有说明,否则此属性从声明的父级继承。开箱即用,这是null一个Window控件。

这个问题有两种选择。您可以DataContext在代码隐藏或 XAML 中显式设置

// In XAML
<Window DataContext={Binding RelativeSource={RelativeSource Self}}>

or

// In the code-behind
DataContext = this;

另一个问题是绑定是在初始化时应用的。最初,您的属性是空的。在该InitializeComponent阶段之后,控件将“绑定”到属性(为空)。之后设置属性时,控件无法知道它已更改。有两种机制可以实现这一点。在控制级别,您可以将这些属性作为DependencyProperty或实现INotifyPropertyChanged接口并引发更改。如果你想走 INPC 路线,你可以这样实现你的属性和窗口:

public partial class MainWindow : INotifyPropertyChanged
{
    private string firstString;
    private string secondString;
    private string thirdString;
    private string fourthString;

    public string FirstString
    {
        get { return firstString; }
        set
        {
            firstString = value;
            RaisePropertyChanged("FirstString");
        }
    }

    public string SecondString
    {
        get { return secondString; }
        set
        {
            secondString = value;
            RaisePropertyChanged("SecondString");
        }
    }

    public string ThirdString
    {
        get { return thirdString; }
        set
        {
            thirdString = value;
            RaisePropertyChanged("ThirdString");
        }
    }

    public string FourthString
    {
        get { return fourthString; }
        set
        {
            fourthString = value;
            RaisePropertyChanged("FourthString");
        }
    }

    public MainWindow()
    {
        DataContext = this;
        InitializeComponent();

        FirstString = "First";
        SecondString = "Second";
        ThirdString = "Third";
        FourthString = "Fourth";
    }

    public event PropertyChangedEventHandler PropertyChanged = delegate { };

    private void RaisePropertyChanged(string propertyName)
    {
        var handlers = PropertyChanged;

        handlers(this, new PropertyChangedEventArgs(propertyName));
    }
}
于 2013-08-05T14:19:41.937 回答
4

除非另有说明,否则绑定的路径是相对于DataContext元素的。就您而言,我怀疑您根本没有指定DataContext...

由于属性是在MainWindow类本身中声明的,因此最简单的修复方法是添加:

DataContext = this;

在构造函数的末尾。

于 2013-08-05T14:10:05.520 回答
3

由于您没有通知FirstString,和 更改SecondString,因此更改不会反映在 UI 中。您可以实施或处理ThirdStringFourthStringINotifyPropertyChangedDependencyProperty

设置你的Datacontext,太。

恕我直言DependencyProperty,更适合这种用途。这是一个例子:

public partial class MainWindow : Window
{
    #region Public
    public string FirstString
    {
        get { return (string)GetValue(FirstStringProperty); }
        set { SetValue(FirstStringProperty, value); }
    }
    public string SecondString
    {
        get { return (string)GetValue(SecondStringProperty); }
        set { SetValue(SecondStringProperty, value); }
    }
    public string ThirdString
    {
        get { return (string)GetValue(ThirdStringProperty); }
        set { SetValue(ThirdStringProperty, value); }
    }
    public string FourthString
    {
        get { return (string)GetValue(FourthStringProperty); }
        set { SetValue(FourthStringProperty, value); }
    }

    #region Dependency Properties
    public static readonly DependencyProperty FirstStringProperty = DependencyProperty.Register("FirstString", typeof(string), typeof(MainWindow), new PropertyMetadata("default value"));
    public static readonly DependencyProperty SecondStringProperty = DependencyProperty.Register("SecondString", typeof(string), typeof(MainWindow), new PropertyMetadata("default value"));
    public static readonly DependencyProperty ThirdStringProperty = DependencyProperty.Register("ThirdString", typeof(string), typeof(MainWindow), new PropertyMetadata("default value"));        
    public static readonly DependencyProperty FourthStringProperty = DependencyProperty.Register("FourthString", typeof(string), typeof(MainWindow), new PropertyMetadata("default value"));
    #endregion
    #endregion

    public MainWindow()
    {
        InitializeComponent();    

        FirstString = "First";
        SecondString = "Second";
        ThirdString= "Third";
        FourthString= "Fourth";

        this.DataContext = this;
    }
}
于 2013-08-05T14:12:01.493 回答
2

我建议您创建另一个类 MainWindowViewModel。

public class MainWindowViewModel
{
    public string FirstString { get; set; }
    public string SecondString { get; set; }
    public string ThirdString { get; set; }
    public string FourthString { get; set; }

    public MainWindowViewModel()
    {    
        FirstString = "First";
        SecondString = "Second";
        ThirdString= "Third";
        FourthString= "Fourth";
    }
}

然后在窗口类上调用 show 之前设置 MainWindow 对象的 DataContext。

MainWindow wnd = new MainWindow();
wnd.DataContext = new MainWindowViewModel();
wnd.Show();

您可以通过从 App.xaml 中删除 StartupUri="MainWindow.xaml" 并通过覆盖 App.xaml.cs 中的 OnStartup 手动创建和显示主窗口来执行此操作。

于 2013-08-05T14:22:47.607 回答
2

它应该是这样的工作,

<Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
                <RowDefinition Height="*"/>
                <RowDefinition Height="*"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <TextBlock x:Name="First" Text="{Binding FirstString}" Grid.Row="0"/>
            <TextBlock x:Name="Second" Text="{Binding SecondString}" Grid.Row="1"/>
            <TextBlock x:Name="Third" Text="{Binding ThirdString}" Grid.Row="2"/>
            <TextBlock x:Name="Fourth" Text="{Binding FourthString}" Grid.Row="3"/>
        </Grid>

和 c# 代码会像,

public string FirstString { get; set; }
public string SecondString { get; set; }
public string ThirdString { get; set; }
public string FourthString { get; set; }

public MainWindow()
{
    InitializeComponent();    

    FirstString = "First";
    SecondString = "Second";
    ThirdString = "Third";
    FourthString= "Fourth";
    this.DataContext = this;  //here you set the context to current instance of window

}
于 2013-08-05T14:24:50.827 回答