WPF双向绑定不起作用

Posted

技术标签:

【中文标题】WPF双向绑定不起作用【英文标题】:WPF two-way binding not working 【发布时间】:2014-04-10 18:49:44 【问题描述】:

我有一个分配给我的主窗口的数据上下文 (UserPreferences),以及一个文本框,它双向绑定到上下文中数据上下文属性 (CollectionDevice) 中的一个属性。

当窗口加载时,文本框不会绑定到我模型中的属性。我在调试器中验证数据上下文是否设置为模型对象,并且模型的属性已正确分配。然而,我得到的只是一系列带有 0 的文本框。

当我在文本框中输入数据时,模型中的数据会更新。问题只是在我加载数据并将其应用于数据上下文时发生,文本框没有得到更新。

当我将模型保存到数据库时,会从文本框中保存正确的数据。当我从数据库中恢复模型时,会应用正确的数据。当模型应用于我的构造函数中的数据上下文时,文本框的数据上下文包含正确的数据,并且它的属性按应有的方式分配。问题是 UI 没有反映这一点。

XAML

<Window.DataContext>
    <models:UserPreferences />
</Window.DataContext>

        <!-- Wrap pannel used to store the manual settings for a collection device. -->
        <StackPanel Name="OtherCollectionDevicePanel">
            <StackPanel Orientation="Horizontal">
                <TextBlock VerticalAlignment="Center" Margin="10, 10, 0, 0" Text="Baud Rate" />
                <TextBox Name="BaudRateTextBox" Text="Binding Path=SelectedCollectionDevice.BaudRate, Mode=TwoWay" Margin="10, 10, 0, 0" MinWidth="80" ></TextBox>
            </StackPanel>
            <WrapPanel>
                <TextBlock VerticalAlignment="Center" Margin="10, 10, 0, 0" Text="Com Port" />
                <TextBox Text="Binding Path=SelectedCollectionDevice.ComPort, Mode=TwoWay" Margin="10, 10, 0, 0" MinWidth="80" ></TextBox>
            </WrapPanel>
            <WrapPanel>
                <TextBlock VerticalAlignment="Center" Margin="10, 10, 0, 0" Text="Data Points" />
                <TextBox Text="Binding Path=SelectedCollectionDevice.DataPoints, Mode=TwoWay" Margin="10, 10, 0, 0" MinWidth="80" ></TextBox>
            </WrapPanel>
            <WrapPanel Orientation="Horizontal">
                <TextBlock VerticalAlignment="Center" Margin="10, 10, 0, 0" Text="WAAS" />
                <CheckBox IsChecked="Binding Path=SelectedCollectionDevice.WAAS, Mode=TwoWay" Content="Enabled" Margin="20, 0, 0, 0" VerticalAlignment="Bottom"></CheckBox>
            </WrapPanel>
        </StackPanel>

模型

/// <summary>
/// Provides a series of user preferences.
/// </summary>
[Serializable]
public class UserPreferences : INotifyPropertyChanged

    private CollectionDevice selectedCollectionDevice;

    public UserPreferences()
    
        this.AvailableCollectionDevices = new List<CollectionDevice>();

        var yuma1 = new CollectionDevice
        
            BaudRate = 4800,
            ComPort = 31,
            DataPoints = 1,
            Name = "Trimble Yuma 1",
            WAAS = true
        ;

        var yuma2 = new CollectionDevice
        
            BaudRate = 4800,
            ComPort = 3,
            DataPoints = 1,
            Name = "Trimble Yuma 2",
            WAAS = true
        ;

        var toughbook = new CollectionDevice
        
            BaudRate = 4800,
            ComPort = 3,
            DataPoints = 1,
            Name = "Panasonic Toughbook",
            WAAS = true
        ;


        var other = new CollectionDevice
        
            BaudRate = 0,
            ComPort = 0,
            DataPoints = 0,
            Name = "Other",
            WAAS = false
        ;

        this.AvailableCollectionDevices.Add(yuma1);
        this.AvailableCollectionDevices.Add(yuma2);
        this.AvailableCollectionDevices.Add(toughbook);
        this.AvailableCollectionDevices.Add(other);

        this.SelectedCollectionDevice = this.AvailableCollectionDevices.First();
    

    /// <summary>
    /// Gets or sets the GPS collection device.
    /// </summary>
    public CollectionDevice SelectedCollectionDevice
    
        get
        
            return selectedCollectionDevice;
        
        set
        
            selectedCollectionDevice = value;
            this.OnPropertyChanged("SelectedCollectionDevice");
        
    

    /// <summary>
    /// Gets or sets a collection of devices that can be used for collecting GPS data.
    /// </summary>
    [Ignore]
    [XmlIgnore]
    public List<CollectionDevice> AvailableCollectionDevices  get; set; 

    public event PropertyChangedEventHandler PropertyChanged;

    /// <summary>
    /// Notifies objects registered to receive this event that a property value has changed.
    /// </summary>
    /// <param name="propertyName">The name of the property that was changed.</param>
    protected virtual void OnPropertyChanged(string propertyName)
    
        if (this.PropertyChanged != null)
        
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        
    

CollectionDevice

/// <summary>
/// CollectionDevice model
/// </summary>
[Serializable]
public class CollectionDevice : INotifyPropertyChanged

    /// <summary>
    /// Gets or sets the COM port.
    /// </summary>
    private int comPort;

    /// <summary>
    /// Gets or sets a value indicating whether [waas].
    /// </summary>
    private bool waas;

    /// <summary>
    /// Gets or sets the data points.
    /// </summary>
    private int dataPoints;

    /// <summary>
    /// Gets or sets the baud rate.
    /// </summary>
    private int baudRate;

    /// <summary>
    /// Gets or sets the name.
    /// </summary>
    public string Name  get; set; 

    /// <summary>
    /// Gets or sets the COM port.
    /// </summary>
    public int ComPort
    
        get
        
            return this.comPort;
        

        set
        
            this.comPort= value;
            this.OnPropertyChanged("ComPort");
        
    

    /// <summary>
    /// Gets or sets the COM port.
    /// </summary>
    public bool WAAS
    
        get
        
            return this.waas;
        

        set
        
            this.waas = value;
            this.OnPropertyChanged("WAAS");
        
    

    /// <summary>
    /// Gets or sets the COM port.
    /// </summary>
    public int DataPoints
    
        get
        
            return this.dataPoints;
        

        set
        
            this.dataPoints = value;
            this.OnPropertyChanged("DataPoints");
        
    

    /// <summary>
    /// Gets or sets the COM port.
    /// </summary>
    public int BaudRate
    
        get
        
            return this.baudRate;
        

        set
        
            this.baudRate = value;
            this.OnPropertyChanged("BaudRate");
        
    

    public event PropertyChangedEventHandler PropertyChanged;

    /// <summary>
    /// Notifies objects registered to receive this event that a property value has changed.
    /// </summary>
    /// <param name="propertyName">The name of the property that was changed.</param>
    protected virtual void OnPropertyChanged(string propertyName)
    
        if (this.PropertyChanged != null)
        
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        
    

    public override string ToString()
    
        return this.Name;
    

有人能指出我正确的方向吗?我认为问题是我在 XAML 中的绑定;我找不到它。我需要它是双向绑定的,因为数据可以在模型内的应用程序生命周期内随时更改(数据库通过同步更新)并且 UI 需要反映这些更改,但用户可以通过以下方式将更改应用于模型用户界面。

更新 1

我尝试强制更新文本框数据绑定,但效果不佳。

BindingExpression be = this.BaudRateTextBox.GetBindingExpression(TextBox.TextProperty);
be.UpdateSource();

我还尝试将UpdateSourceTrigger 设置为PropertyChanged,但这似乎也没有解决问题。

<TextBox Name="BaudRateTextBox" Text="Binding Path=SelectedCollectionDevice.BaudRate, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged" Margin="10, 10, 0, 0" MinWidth="80" ></TextBox>

更新 2

我尝试关注一些documentation from Microsoft,但似乎无法解决问题。加载窗口时,这些值仍保持为 0。从数据库中恢复对象的状态后,绑定没有更新。绑定是连线的,因为当我输入数据时,数据上下文会更新。出于某种原因,当我将其设置为双向时,它的行为就像单向一样。

更新 3

我试图将代码移到窗口加载事件中并移出构造函数,但这似乎没有帮助。我发现有趣的是,在反序列化过程中不会触发 PropertyChanged 事件。在这种情况下,我认为这并不重要,因为对象已正确完全恢复,然后无论如何我只是将其分配给数据上下文。我将数据上下文移出 XAML 并移入 WindowLoaded 以测试 XAML 是否是问题所在。结果是一样的。

private void WindowLoaded(object sender, RoutedEventArgs e)

    // Restore our preferences state.
    var preferences = new UserPreferenceCommands();
    Models.UserPreferences viewModel = new Models.UserPreferences();

    // Set up the event handler before we deserialize.
    viewModel.PropertyChanged += viewModel_PropertyChanged;
    preferences.LoadPreferencesCommand.Execute(viewModel);

    // At this point, viewModel is a valid object. All properties are set correctly.
    viewModel = preferences.Results;

    // After this step, the UI still shows 0's in all of the text boxs. Even though the values are not zero.
    this.DataContext = viewModel;


// NEVER gets fired from within the WindowLoaded event.
void viewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)

    MessageBox.Show("Property changed!");


// This changes the model properties and is immediately reflected in the UI. Why does this not happen within the WindowLoaded event?
private void TestButtonClickEvent(object sender, RoutedEventArgs e)

    var context = this.DataContext as Models.UserPreferences;
    context.SelectedCollectionDevice.ComPort = 1536;

更新 4 - 发现问题

我已发现问题,但仍需要解决方案。数据绑定的重点是让我不必执行此手动分配。我的 INotify 实现有问题吗?

private void WindowLoaded(object sender, RoutedEventArgs e)

    // Restore our preferences state.
    var preferences = new UserPreferenceCommands();
    Models.UserPreferences viewModel = new Models.UserPreferences();

    // Set up the event handler before we deserialize.
    viewModel.PropertyChanged += viewModel_PropertyChanged;
    preferences.LoadPreferencesCommand.Execute(viewModel);

    // At this point, viewModel is a valid object. All properties are set correctly.
    viewModel = preferences.Results;

    // After this step, the UI still shows 0's in all of the text boxs. Even though the values are not zero.
    this.DataContext = viewModel;

    // SOLUTION: - Setting the actual property causes the UI to be reflected when the window is initialized; setting the actual data context does not. Why? Also note that I set this property and my PropertyChanged event handler still does not fire.
    ((Models.UserPreferences) DataContext).SelectedCollectionDevice = viewModel.SelectedCollectionDevice;


【问题讨论】:

我要做的第一件事是将List&lt;CollectionDevice&gt;更改为ObservableCollection&lt;CollectionDevice&gt; 我什至还有一个自定义类支持多线程更新:code.google.com/p/mutinyirc/source/browse/MvvmFoundation.Wpf/… 以备不时之需。 感谢@Ekoostik,我之所以没有将其作为可观察的,是因为它不需要被观察。集合是在对象构造函数中设置的,它永远不会改变。它充满了预先确定的收集设备。最后一个条目是名称设置为“其他”的设备,然后允许用户输入自定义数据。这就是为什么观察到 SelectedCollectionDevice 而不是观察到 AvailableCollectionDevice。 我复制/粘贴了您的代码,没有任何更改,一切正常。唯一的区别......我只是在后面的代码中手动分配了 Window 构造函数中的数据上下文。我什至添加了一个更新 SelectedCollectionDevice 上的属性的按钮,并且视图反映了更改。我不知道问题是什么,但希望这可以帮助您调试它。 嗨@Kevin 感谢您的提示。我尝试向 UI 添加一个按钮,并让它为SelectedCollectionDevice 中的一个属性分配一个值,并在 UI 上正确更新。我还将数据上下文从 XAML 移到构造函数中。最终结果是在 Window 加载时对象仍未正确绑定。文本框是空的。加载窗口后的任何更新都会立即显示在文本框上,而不是在加载期间。还有其他想法吗? 【参考方案1】:

默认情况下,TextBox 的Text 属性只有在失去焦点时才会更新。 你用你的 DataContext 验证了吗?

如果您想覆盖此行为,您必须以这种方式包含 UpdateSourceTrigger 属性:

Text="Binding Path=SelectedCollectionDevice.BaudRate, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged

UpdateSourceTrigger的值设置为PropertyChanged,当您更改绑定属性的值时,该更改会在文本更改时反映在文本框中。

关于UpdateSourceTrigger属性使用的有用教程是here。

【讨论】:

我实际上刚刚更新了我的 OP,提到我已经尝试过这个。出于某种原因,Text 没有设置为属性值,即使值存在于上下文中。我不知所措。我什至使用 this.txtBaudRate.DataContext = userPreferences; 对数据上下文进行了硬重新分配。在视图构造函数中(在 userPreferences 被反序列化之后)并且文本框的值保持为 0。 @JohnathonSullinger 抱歉我的回复晚了!我很高兴你修好了! +1 这个答案的简单性和高度概括性帮助了我。我想要默认行为,只是有Binding MyProp,但必须将Mode 显式设置为TwoWay,并将UpdateSourceTrigger 设置为LostFocus 我的情况是,我也像 selectedRow 一样绑定上面的绑定。但是我有一些问题,当清除我的文本框时,我会点击取消。它将重定向到主用户控件。之后,我将双击同一行,文本框为空白。在这里,我不保存记录。它需要从模型选择的行对象中获取值并绑定到文本框。但是,它失败了。这里需要做什么?你能澄清一下吗?谢谢! 没有意识到“文本框只有在失去焦点时才会更新”谢谢!现在我终于明白为什么有些例子显示了 UpdateSourceTrigger 而有些没有。【参考方案2】:

好的,我能够确定问题并解决它。结果是造成这种情况的原因的汇编。

首先,我的模型。

UserPreferences

[Serializable]
public class UserPreferences : INotifyPropertyChanged

    private CollectionDevice selectedCollectionDevice;

    public UserPreferences()
    
        this.AvailableCollectionDevices = new List<CollectionDevice>();

        var yuma1 = new CollectionDevice
        
            BaudRate = 4800,
            ComPort = 31,
            DataPoints = 1,
            Name = "Trimble Yuma 1",
            WAAS = true
        ;

        var yuma2 = new CollectionDevice
        
            BaudRate = 4800,
            ComPort = 3,
            DataPoints = 1,
            Name = "Trimble Yuma 2",
            WAAS = true
        ;

        var toughbook = new CollectionDevice
        
            BaudRate = 4800,
            ComPort = 3,
            DataPoints = 1,
            Name = "Panasonic Toughbook",
            WAAS = true
        ;


        var other = new CollectionDevice
        
            BaudRate = 0,
            ComPort = 0,
            DataPoints = 0,
            Name = "Other",
            WAAS = false
        ;

        this.AvailableCollectionDevices.Add(yuma1);
        this.AvailableCollectionDevices.Add(yuma2);
        this.AvailableCollectionDevices.Add(toughbook);
        this.AvailableCollectionDevices.Add(other);

        this.SelectedCollectionDevice = this.AvailableCollectionDevices.First();
    

    /// <summary>
    /// Gets or sets the GPS collection device.
    /// </summary>
    public CollectionDevice SelectedCollectionDevice
    
        get
        
            return selectedCollectionDevice;
        
        set
        
            selectedCollectionDevice = value;

            if (selectedCollectionDevice.Name == "Other")
            
                this.AvailableCollectionDevices[3] = value;
            

            this.OnPropertyChanged("SelectedCollectionDevice");
        
    

    /// <summary>
    /// Gets or sets a collection of devices that can be used for collecting GPS data.
    /// </summary>
    [Ignore]
    [XmlIgnore]
    public List<CollectionDevice> AvailableCollectionDevices  get; set; 

    public event PropertyChangedEventHandler PropertyChanged;

    /// <summary>
    /// Notifies objects registered to receive this event that a property value has changed.
    /// </summary>
    /// <param name="propertyName">The name of the property that was changed.</param>
    protected virtual void OnPropertyChanged(string propertyName)
    
        if (this.PropertyChanged != null)
        
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        
    

SelectedCollectionDevice 的设置器中,我并不想查看所选设备是否为其他。所有其他设备(yuma1、松下等)都具有永远不会更改的预定属性值。当用户选择“其他”时,会显示文本框,他们可以手动输入数据。问题是在窗口加载期间从数据库中恢复手动输入的数据时,我没有将SelectedCollectionDevice中的自定义数据分配给集合中的相应对象。

在窗口加载期间,Combobox.SelectedItem 被设置为SelectedCollectionDevice 的索引。 Combobox.ItemsSource 设置为 AvailableCollectionDevices 集合。

this.CollectionDevice.SelectedIndex = 
    viewModel.AvailableCollectionDevices.IndexOf(
        viewModel.AvailableCollectionDevices.FirstOrDefault(
            acd => acd.Name == viewModel.SelectedCollectionDevice.Name));

执行上述代码时,组合框会从其数据源中提取默认对象,该对象的所有值都设置为零。在组合框的 SelectionChanged 事件中,我将数据上下文 SelectedCollectionDevice 分配给与组合框关联的清零项目。

private void CollectionDeviceSelected(object sender, SelectionChangedEventArgs e)

    if (e.AddedItems.Count > 0 && e.AddedItems[0] is CollectionDevice)
    
        // Assign the view models SelectedCollectionDevice to the device selected in the combo box.
        var device = e.AddedItems[0] as CollectionDevice;
        ((Models.UserPreferences)this.DataContext).SelectedCollectionDevice = device;

        // Check if Other is selected. If so, we have to present additional options.
        if (device.Name == "Other")
        
            OtherCollectionDevicePanel.Visibility = Visibility.Visible;
        
        else if (OtherCollectionDevicePanel.Visibility == Visibility.Visible)
        
            OtherCollectionDevicePanel.Visibility = Visibility.Collapsed;
        
    

长话短说,我在SelectedCollectionDevice 的设置器中添加了上面的代码,以将值应用于AvailableCollectionDevices 列表。这样,当组合框选择了“其他”值时,它会从具有正确数据的集合中提取值。在反序列化期间,我只是反序列化 SelectedCollectionDevice 而不是 List,这就是为什么在首次加载窗口时数据总是被覆盖的原因。

这也解释了为什么使用本地 viewModel.SelectedCollectionDevice 重新分配数据上下文 SelectedCollectionDevice 属性有效。我正在替换与组合框关联的清零对象,该组合框在SelectionChanged 事件期间设置了数据上下文。我无法在 XAML 中设置 DataContext 并删除手动分配。

感谢所有帮助,它帮助我缩小了调试范围,直到我最终解决了问题。非常感谢!

【讨论】:

【参考方案3】:

不是答案,但想发布在我的机器上运行的代码以帮助 OP...

完整的 xaml 页面...

<Window x:Class="WpfApplication1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" Height="300" Width="300">
    <Grid>
        <StackPanel Name="OtherCollectionDevicePanel">
            <StackPanel Orientation="Horizontal">
                <TextBlock VerticalAlignment="Center"
                           Margin="10, 10, 0, 0"
                           Text="Baud Rate" />
                <TextBox Name="BaudRateTextBox"
                         Text="Binding Path=SelectedCollectionDevice.BaudRate, Mode=TwoWay"
                         Margin="10, 10, 0, 0"
                         MinWidth="80"></TextBox>
            </StackPanel>
            <WrapPanel>
                <TextBlock VerticalAlignment="Center"
                           Margin="10, 10, 0, 0"
                           Text="Com Port" />
                <TextBox Text="Binding Path=SelectedCollectionDevice.ComPort, Mode=TwoWay"
                         Margin="10, 10, 0, 0"
                         MinWidth="80"></TextBox>
            </WrapPanel>
            <WrapPanel>
                <TextBlock VerticalAlignment="Center"
                           Margin="10, 10, 0, 0"
                           Text="Data Points" />
                <TextBox Text="Binding Path=SelectedCollectionDevice.DataPoints, Mode=TwoWay"
                         Margin="10, 10, 0, 0"
                         MinWidth="80"></TextBox>
            </WrapPanel>
            <WrapPanel Orientation="Horizontal">
                <TextBlock VerticalAlignment="Center"
                           Margin="10, 10, 0, 0"
                           Text="WAAS" />
                <CheckBox IsChecked="Binding Path=SelectedCollectionDevice.WAAS, Mode=TwoWay"
                          Content="Enabled"
                          Margin="20, 0, 0, 0"
                          VerticalAlignment="Bottom"></CheckBox>
            </WrapPanel>
            <Button Click="ButtonBase_OnClick" Content="Set ComPort to 11"></Button>
        </StackPanel>
    </Grid>
</Window>

后面的完整代码...

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Xml.Serialization;

namespace WpfApplication1

    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    
        public Window1()
        
            InitializeComponent();
            DataContext = new UserPreferences();
        

        private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
        
            ((UserPreferences) DataContext).SelectedCollectionDevice.ComPort = 11;
        

    

    /// <summary>
    /// Provides a series of user preferences.
    /// </summary>
    [Serializable]
    public class UserPreferences : INotifyPropertyChanged
    
        private CollectionDevice selectedCollectionDevice;

        public UserPreferences()
        
            this.AvailableCollectionDevices = new List<CollectionDevice>();

            var yuma1 = new CollectionDevice
            
                BaudRate = 4800,
                ComPort = 31,
                DataPoints = 1,
                Name = "Trimble Yuma 1",
                WAAS = true
            ;

            var yuma2 = new CollectionDevice
            
                BaudRate = 4800,
                ComPort = 3,
                DataPoints = 1,
                Name = "Trimble Yuma 2",
                WAAS = true
            ;

            var toughbook = new CollectionDevice
            
                BaudRate = 4800,
                ComPort = 3,
                DataPoints = 1,
                Name = "Panasonic Toughbook",
                WAAS = true
            ;


            var other = new CollectionDevice
            
                BaudRate = 0,
                ComPort = 0,
                DataPoints = 0,
                Name = "Other",
                WAAS = false
            ;

            this.AvailableCollectionDevices.Add(yuma1);
            this.AvailableCollectionDevices.Add(yuma2);
            this.AvailableCollectionDevices.Add(toughbook);
            this.AvailableCollectionDevices.Add(other);

            this.SelectedCollectionDevice = this.AvailableCollectionDevices.First();
        

        /// <summary>
        /// Gets or sets the GPS collection device.
        /// </summary>
        public CollectionDevice SelectedCollectionDevice
        
            get
            
                return selectedCollectionDevice;
            
            set
            
                selectedCollectionDevice = value;
                this.OnPropertyChanged("SelectedCollectionDevice");
            
        

        /// <summary>
        /// Gets or sets a collection of devices that can be used for collecting GPS data.
        /// </summary>
        [XmlIgnore]
        public List<CollectionDevice> AvailableCollectionDevices  get; set; 

        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Notifies objects registered to receive this event that a property value has changed.
        /// </summary>
        /// <param name="propertyName">The name of the property that was changed.</param>
        protected virtual void OnPropertyChanged(string propertyName)
        
            if (this.PropertyChanged != null)
            
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            
        
    

    /// <summary>
    /// CollectionDevice model
    /// </summary>
    [Serializable]
    public class CollectionDevice : INotifyPropertyChanged
    
        /// <summary>
        /// Gets or sets the COM port.
        /// </summary>
        private int comPort;

        /// <summary>
        /// Gets or sets a value indicating whether [waas].
        /// </summary>
        private bool waas;

        /// <summary>
        /// Gets or sets the data points.
        /// </summary>
        private int dataPoints;

        /// <summary>
        /// Gets or sets the baud rate.
        /// </summary>
        private int baudRate;

        /// <summary>
        /// Gets or sets the name.
        /// </summary>
        public string Name  get; set; 

        /// <summary>
        /// Gets or sets the COM port.
        /// </summary>
        public int ComPort
        
            get
            
                return this.comPort;
            

            set
            
                this.comPort = value;
                this.OnPropertyChanged("ComPort");
            
        

        /// <summary>
        /// Gets or sets the COM port.
        /// </summary>
        public bool WAAS
        
            get
            
                return this.waas;
            

            set
            
                this.waas = value;
                this.OnPropertyChanged("WAAS");
            
        

        /// <summary>
        /// Gets or sets the COM port.
        /// </summary>
        public int DataPoints
        
            get
            
                return this.dataPoints;
            

            set
            
                this.dataPoints = value;
                this.OnPropertyChanged("DataPoints");
            
        

        /// <summary>
        /// Gets or sets the COM port.
        /// </summary>
        public int BaudRate
        
            get
            
                return this.baudRate;
            

            set
            
                this.baudRate = value;
                this.OnPropertyChanged("BaudRate");
            
        

        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Notifies objects registered to receive this event that a property value has changed.
        /// </summary>
        /// <param name="propertyName">The name of the property that was changed.</param>
        protected virtual void OnPropertyChanged(string propertyName)
        
            if (this.PropertyChanged != null)
            
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            
        

        public override string ToString()
        
            return this.Name;
        
    

【讨论】:

我使用了我的代码并对其进行了调整,以便在构造函数中创建数据上下文,就像你的那样,以排除我的上下文的 XAML 问题。问题继续存在。我可以在窗口初始化后进行更改并看到它们反映在 UI 上。但是,在退出构造函数之前对我的对象所做的任何更改都不会反映到 UI。 我还将我的恢复代码和赋值从构造函数移到了窗口的 Loaded 事件中,以查看它是否只是时间问题。在 Loaded 事件结束时,我将有效对象分配给数据上下文,UI 仍然只显示零。 我明白了...我刚刚提到您可以尝试更改页面加载之外的属性...但后来看到此评论并删除了我上面的评论。 对象从保存状态反序列化,然后分配给数据上下文。在我执行该分配时,该对象已完全反序列化并具有有效的属性值。我对此感到非常茫然。 当我执行反序列化时,我的 PropertyChanged 事件不会被触发。这和它有关系吗?我认为它不会考虑在对象被正确反序列化并且属性设置正确之后将对象分配为我的数据上下文。我想我还是会提到这一点,以防它可能产生一些影响。【参考方案4】:

我有同样的问题。我的问题是绑定属性名称错误。如果您查看输出窗口,您可以在运行时看到所有绑定错误。

System.Windows.Data 错误:40:BindingExpression 路径错误: 在“对象”上找不到“SelectedProtectedWebsiteTemplate”属性 ''ProtectedWebsitesViewModel' (HashCode=32764015)'。 BindingExpression:Path=SelectedProtectedWebsiteTemplate.Name; DataItem='ProtectedWebsitesViewModel' (HashCode=32764015);目标 元素是'TextBox'(名称='');目标属性是“文本”(类型 '字符串')

【讨论】:

以上是关于WPF双向绑定不起作用的主要内容,如果未能解决你的问题,请参考以下文章

使用 ngModel 的 Angular 2 双向绑定不起作用

使用 ngModel 的 Angular 2 双向绑定不起作用

使用 ngModel 的 Angular 2 双向绑定不起作用

使用 ngModel 的 Angular 2 双向绑定不起作用

Angular - 来自@Input的双向数据绑定不起作用

在协程内触发时双向数据绑定不起作用