WPF双向绑定无法工作

22
我有一个数据上下文(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需要反映这些更改,但用户也可以通过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

我尝试遵循一些来自 Microsoft 的文档,但似乎没有解决问题。窗口加载时数值仍然保持为0。在从数据库还原对象的状态后,绑定未被更新。虽然绑定已经连接好了,因为当我输入数据时,数据上下文得到了更新。由于某种原因,它表现得像单向绑定,尽管我把它设置成了双向绑定。

更新3

我尝试将代码移到窗口加载事件中并脱离构造函数,但这似乎没有帮助。我发现有趣的是,属性更改事件在反序列化过程中没有触发。在这种情况下,我不认为这有什么关系,因为对象已经完全恢复并且我只是将其分配给了数据上下文。我将数据上下文移出 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<CollectionDevice> 更改为 ObservableCollection<CollectionDevice> - EkoostikMartin
1
如果需要的话,我甚至使用了一个自定义类来支持多线程更新:https://code.google.com/p/mutinyirc/source/browse/MvvmFoundation.Wpf/MTObservableCollection.cs - Rodrigo Silva
感谢 @Ekoostik,我没有将其设置为 Observable 是因为它不需要被观察。该集合是在对象的构造函数中设置的,并且永远不会更改。它填充了预先确定的集合设备。最后一个条目是一个名称设置为“其他”的设备,然后允许用户输入自定义数据。这就是为什么 SelectedCollectionDevice 被观察但 AvailableCollectionDevice 却不被观察的原因。 - Johnathon Sullinger
对我来说不合理的是,为什么它在我的测试窗口中加载时会正确绑定...但你设置的方式却不行。 - Kevin
@Kevin 你说得对。问题只出现在窗口加载时。一旦窗口加载完成,更改就会反映出来。 - Johnathon Sullinger
显示剩余6条评论
4个回答

72

默认情况下,当焦点离开时,TextBox的 您是否已经通过您的DataContext进行了验证?

如果您想覆盖此行为,您需要以以下方式包含UpdateSourceTrigger属性:

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

UpdateSourceTrigger的值设置为PropertyChanged,当您更改绑定属性的值时,文本框中的更改会立即反映出来。这样做可以使文本在文本更改时立即同步更新。
关于如何使用UpdateSourceTrigger属性的有用教程在这里

我实际上已经更新了我的OP,提到我尝试过这个方法。出于某种原因,尽管上下文中存在值,文本并没有设置为属性值。我很困惑。我甚至在视图的构造函数中执行了数据上下文的硬重新分配,使用此.txtBaudRate.DataContext = userPreferences; (在对userPreferences进行反序列化之后),但文本框仍保持为0的值。 - Johnathon Sullinger
@JohnathonSullinger 对不起,我回复晚了!很高兴你解决了问题! - Alberto Solano
2
+1 这个答案的简洁性和高度概括帮助了我。我想要默认行为,只需使用 {Binding MyProp},但必须显式设置 ModeTwoWayUpdateSourceTriggerLostFocus - Assimilater
我的情况是,我也像上面绑定选定行一样进行绑定。但是我有一些问题,当清除我的文本框并单击取消时,它将重定向到主用户控件。之后,我会双击同一行,文本框为空。在这里,我没有保存记录。它需要从模型选定的行对象中获取值并绑定到文本框。但是,它失败了。这里需要做什么?您能否请澄清一下。谢谢! - kasim
读取第一行就足够了:D - bilal.haider
1
之前没有意识到“只有在失去焦点时,TextBox 才会更新”。谢谢!现在我终于明白为什么有些示例显示 UpdateSourceTrigger,而有些则不显示。 - dvdhns

6

好的,我已经确定了问题并解决了它。原来是一系列问题导致的。

首先,是我的模型。

UserPreferences <-- MainWindow 与此数据绑定。

[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的setter中,我没有考虑所选设备是否为“其他”。所有其他设备(如yuma1、panasonic等)都有预先确定的属性值,这些值永远不会更改。当用户选择“其他”时,文本框将显示出来,他们可以手动输入数据。问题在于,在窗口加载期间从数据库中恢复手动输入的数据时,我没有将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的Setter中添加了上面的代码,将值应用于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上。 - Johnathon Sullinger
我还将我的恢复代码和值分配从构造函数中移出,并移到窗口的Loaded事件中,以便查看是否只是时间问题。在Loaded事件结束时,我将有效对象分配给数据上下文,但UI仍然只显示零。 - Johnathon Sullinger
我明白了... 我刚才提到你可以尝试在页面加载之外更改属性... 但是看到这个评论后,我删除了上面的评论。 - Kevin
当我执行反序列化时,我的PropertyChanged事件没有被触发。这是否与此有关?我认为不会,因为在对象正确反序列化并且属性正确设置后,我将对象分配为我的数据上下文。无论如何,我想提一下,以防它可能产生影响。 - Johnathon Sullinger
你有测试过在窗口构造函数中的Update 4 - WindowLoaded()代码吗?如果没有,我建议将该代码放在构造函数中,并设置你的数据上下文,然后不要设置SelectedCollectionDevice。 - Kevin
显示剩余5条评论

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

System.Windows.Data Error: 40 : BindingExpression path error: 'SelectedProtectedWebsiteTemplate' property not found on 'object' ''ProtectedWebsitesViewModel' (HashCode=32764015)'. BindingExpression:Path=SelectedProtectedWebsiteTemplate.Name; DataItem='ProtectedWebsitesViewModel' (HashCode=32764015); target element is 'TextBox' (Name=''); target property is 'Text' (type 'String')


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