简体   繁体   English

WPF XAML 绑定不更新

[英]WPF XAML binding does not update

I have a WPF project in which I have 4 TextBlock .我有一个 WPF 项目,其中有 4 个TextBlock What I want is to change the Text of each TextBlock via Binding .我想要的是通过Binding更改每个TextBlockText

So far I have my XAML:到目前为止,我有我的 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>

And in my code I have:在我的代码中,我有:

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

But the Binding doesn't work at all.但是Binding根本不起作用。 Am I doing something wrong?难道我做错了什么?

EDIT: After following Chris Mantle's suggestion in the comments to look at the debug output (I had to enable Warnings for the Binding), I get the following error:编辑:按照 Chris Mantle 在评论中的建议查看调试输出(我必须为绑定启用警告)后,我收到以下错误:

System.Windows.Data Information: 10 : Cannot retrieve value using the binding and no valid fallback value exists; System.Windows.Data 信息:10:无法使用绑定检索值并且不存在有效的回退值; using default instead.改为使用默认值。 BindingExpression:Path=FirstString; BindingExpression:Path=FirstString; DataItem=null;数据项=空; target element is 'TextBlock' (Name='First');目标元素是 'TextBlock' (Name='First'); target property is 'Text' (type 'String')目标属性是“文本”(类型“字符串”)

There are a few things that are incorrect.有几件事是不正确的。 The Binding markup will look at the object in the DataContext property of the control. Binding标记将查看控件的DataContext属性中的对象。 This property inherits the DataContext from the declaring parent unless otherwise specified.除非另有说明,否则此属性从声明的父级继承DataContext Out of the box, this is null for a Window control.开箱即用,对于Window控件,这是null

There are two options for this problem.这个问题有两种选择。 You can either set the DataContext explicitely in the code-behind or the XAML您可以在代码隐藏或 XAML 中显式设置DataContext

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

or

// In the code-behind
DataContext = this;

Another problem is that the binding is applied at initialization.另一个问题是绑定是在初始化时应用的。 Initially, your properties are empty.最初,您的属性是空的。 After the InitializeComponent phase, the controls will "bind" to the properties (which are empty).InitializeComponent阶段之后,控件将“绑定”到属性(为空)。 When you set your properties afterward, the controls have no way to know it has changed.之后设置属性时,控件无法知道它已更改。 There are two mechanism to allow this.有两种机制可以实现这一点。 On the control level, you can make these properties as DependencyProperty or implement the INotifyPropertyChanged interface and raise the changes.在控件级别,您可以将这些属性设为DependencyProperty或实现INotifyPropertyChanged接口并引发更改。 If you want to go the INPC route, you can implement your properties and Window as such:如果你想走 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));
    }
}

Unless specified otherwise, the path of the binding is relative to the DataContext of the element.除非另有说明,否则绑定的路径是相对于元素的DataContext的。 In your case, I suspect you didn't specify a DataContext at all...在您的情况下,我怀疑您根本没有指定DataContext ......

Since the properties are declared in the MainWindow class itself, the easiest fix is to add:由于属性是在MainWindow类本身中声明的,最简单的解决方法是添加:

DataContext = this;

at the end of the constructor.在构造函数的末尾。

Since your aren't notifying that FirstString , SecondString , ThirdString and FourthString changed, the change will not be reflected in the UI.由于您未通知FirstStringSecondStringThirdStringFourthString更改,因此更改将不会反映在 UI 中。 You can either implement INotifyPropertyChanged , or deal with DependencyProperty您可以实现INotifyPropertyChanged ,或处理DependencyProperty

Set your Datacontext , too.也设置你的Datacontext

IMHO DependencyProperty is better for this use.恕我直言, DependencyProperty更适合这种用途。 Here is an example :这是一个例子:

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

I would suggest that you create another class MainWindowViewModel.我建议您创建另一个类 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";
    }
}

Then set the DataContext of the MainWindow object before calling show on the window class.然后在窗口类上调用 show 之前设置 MainWindow 对象的 DataContext。

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

You can do this last bit by removing StartupUri="MainWindow.xaml" from your App.xaml and create and show the main window manually by overriding OnStartup in App.xaml.cs.您可以通过从 App.xaml 中删除 StartupUri="MainWindow.xaml" 并通过覆盖 App.xaml.cs 中的 OnStartup 手动创建和显示主窗口来完成最后一点。

it should be something like this to work,它应该是这样的工作,

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

and c# code will be like,和 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

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM