WPF two-way binding not working

79,287

Solution 1

Alright, I was able to determine the problem and get it resolved. It turned out to be a compilation of things causing this.

First, my model.

UserPreferences <-- MainWindow is data bound to this.

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

In the setter for the SelectedCollectionDevice I was not looking to see if the selected device was other. All of the other devices (yuma1, panasonic etc) have pre-determined property values that are never changed. When the user selects "Other" the textbox's are displayed and they can manually enter the data. The problem was that when the manually entered data was restored from the database during the window loading, I was not assigning the custom data in SelectedCollectionDevice to the corresponding object in the collection.

During window load, the Combobox.SelectedItem was set to the index of the SelectedCollectionDevice. The Combobox.ItemsSource was set to the AvailableCollectionDevices collection.

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

When the above code is executed, the combo box pulls the default object from its data source, which has all of the values set to zero. Within the combo box's SelectionChanged event I assigned the Data Context SelectedCollectionDevice to the zero'd out item associated with the combo box.

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

So long story short, I added the code above in the setter for the SelectedCollectionDevice to apply the value to the AvailableCollectionDevices List<>. This way, when the combo box has the "Other" value selected, it pulls the value from the collection with the correct data. During deserialization, I am just deserializing the SelectedCollectionDevice and not the List<> which is why the data was always being overwrote when the window first loaded.

This also explains why re-assigning the the data context SelectedCollectionDevice property with the local viewModel.SelectedCollectionDevice was working. I was replacing the zero'd out object associated with the combo box, which had set the data context during the SelectionChanged event. I am not able to set the DataContext in the XAML and remove the manual assignment.

Thanks for all of the help, it helped me narrow down my debugging until I finally resolved the issue. Much appreciated!

Solution 2

By default, the Text property of TextBox is updated only when the focus on it is lost. Did you verify it with your DataContext?

If you want to override this behaviour, you have to include the UpdateSourceTrigger property in this way:

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

Setting UpdateSourceTrigger's value to PropertyChanged, the change is reflected in the TextBox when you change the value of your bound property, as soon as the text changes.

A useful tutorial about the usage of UpdateSourceTrigger property is here.

Solution 3

Not an answer but wanted to post the code that works on my machine to help OP...

Complete xaml page...

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

Complete code behind...

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

Solution 4

I had the same issue. My problem was binding property name was wrong. If you look at the output window you can see all the binding errors during runtime.

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

Share:
79,287

Related videos on Youtube

Johnathon Sullinger
Author by

Johnathon Sullinger

Johnathon works full-time as an Enterprise Infrastructure Sr. Manager, responsible for 3 physical data centers and an enterprise presence on public cloud platforms. He manages the teams responsible for the server and network infrastructure along with the application development and support teams and teams adopting new cloud technologies. His current mission is migrating data centers to the public cloud and fostering a culture of DevOps within his organization. In his spare time he works on open source projects he has hosted on GitHub. Has builds software applications that target both Microsoft's .NET Core and NodeJS. You can follow him on Twitter

Updated on January 29, 2021

Comments

  • Johnathon Sullinger
    Johnathon Sullinger over 3 years

    I have a data context (UserPreferences) assigned to my main window, and a textbox that binds two-way to a property within one of the data context's properties (CollectionDevice) within the context.

    When the Window loads, the textbox's do not bind to the properties in my model. I verify within the debugger that the data context is set to the model object and the model's properties are properly assigned. All I get however are a series of textbox's with 0's in them.

    When I enter the data into the textbox's, the data is updated in the model. The issue just happens when I load the data and apply it to the data context, the text box does not get updated.

    When I save the model to the database, the proper data gets saved from the textbox. When I restore the model from the database, the proper data gets applied. When the model is applied to the data context within my constructor, the textbox's datacontext contains the correct data and it's properties are assigned as they should be. The issue is the UI does not reflect this.

    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>
    

    Model <-- Datacontext.

    /// <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 <-- Where text box binds to.

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

    Can someone point me in the right direction? I assume the issue is my binding in XAML; I can't find it though. I need it to be two-way bound because the data can change at any time during the apps lifetime within the model (database is updated through syncs) and the UI needs to reflect those changes, yet the user can apply changes to the model via the UI.

    Update 1

    I tried to force the text box databind to be updated, but that did not work as well.

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

    I also tried setting the UpdateSourceTrigger to PropertyChanged and that did not seem to resolve the problem either.

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

    Update 2

    I tried to follow along with some documentation from Microsoft and it does not seem to fix the issue. The values still remain 0 when the window loads. The binding is not being updated after I restore the state of the object from the database. Binding is wired up though because as I enter data, the data context is updated. For some reason, it's acting like One-Way when I have it set to Two-Way.

    Update 3

    I tried to move the code into the window loaded event and out of the constructor but that did not appear to help. Something I found interesting is that the PropertyChanged event does not get fired during the deserialization process. I don't think it matters in this case because the object is fully restored properly and then I just assign it to the data context anyway. I moved the data context out of the XAML and into the WindowLoaded in order to test if the XAML was the problem. The result was the same.

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

    Update 4 - Problem identified

    I have identified the problem, but still need a resolution. The whole point of data binding is so that I don't have to perform this manual assignment. Is there something wrong with my INotify implementations?

    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;
    
    }
    
    • EkoostikMartin
      EkoostikMartin about 10 years
      First thing I would do is change List<CollectionDevice> to ObservableCollection<CollectionDevice>
    • Rodrigo Silva
      Rodrigo Silva about 10 years
      There's even a custom class I use which supports multi-threaded updates: code.google.com/p/mutinyirc/source/browse/MvvmFoundation.Wpf‌​/… In case you need it.
    • Johnathon Sullinger
      Johnathon Sullinger about 10 years
      Thanks @Ekoostik, the reason I didn't do it as an observable is because it does not need to be observed. The collection is set up within the objects constructor and it is never changing. It's filled with pre-determined collection devices. The last entry is a device with Name set to "Other" which then allows the user to enter custom data. That is why the SelectedCollectionDevice is observed but not the AvailableCollectionDevice.
    • Kevin
      Kevin about 10 years
      I copy / pasted your code with no changes and it all works just fine. The only difference... I just manually assigned the datacontext in the Window constructor in the code behind. I even added a button that updates a property on the SelectedCollectionDevice and the view reflects the change. I don't know what the problem is but hopefully this can help you debug it.
    • Johnathon Sullinger
      Johnathon Sullinger about 10 years
      Hi @Kevin thanks for the tip. I tried adding a button to the UI and having it assign a value to one of the properties within SelectedCollectionDevice and it updated on the UI correctly. I also moved the data context from the XAML and into the constructor. The end result is that the object is still not bound properly when the Window loads. The text box's are empty. Any updates after the Window is loaded is immediately represented on the text box's, just not during loading. Any other ideas?
    • Kevin
      Kevin about 10 years
      What doesn't make sense to me is why it would be bound correctly when it loads in my test window... but not the way you have it setup.
    • Kevin
      Kevin about 10 years
      So just to be clear about it... You are only seeing a binding problem when the window loads right? If the model gets updated then the view reflects those changes properly?
    • Johnathon Sullinger
      Johnathon Sullinger about 10 years
      @Kevin You are correct. The issue is only when the Window is loaded. Once the window is loaded, changes are reflected.
    • GazTheDestroyer
      GazTheDestroyer about 10 years
      Can you remove <Window.DataContext><models:UserPreferences /></Window.DataContext> from your XAML. Although WindowLoaded should replace it, I am wondering whether it's getting set afterwards somehow
    • Johnathon Sullinger
      Johnathon Sullinger about 10 years
      @GazTheDestroyer Sorry I didn't mention it in my updates, (I stated it below in another comment) I had already removed the data context from the xaml. I'll mention that in my updates.
    • GazTheDestroyer
      GazTheDestroyer about 10 years
      Can you try Window.Initialized instead of Window.Loaded?
  • Johnathon Sullinger
    Johnathon Sullinger about 10 years
    I had actually just updated my OP to mention that I had tried this. For some reason, the Text does not get set to the property values, even though the values are there within the context. I'm at a loss. I have even performed a hard re-assignment of the data context using this.txtBaudRate.DataContext = userPreferences; within the views constructor (after userPreferences is deserialized) and the text box's keep a value of 0.
  • Johnathon Sullinger
    Johnathon Sullinger about 10 years
    I took my code and adjusted it so that the data context was created within the constructor like yours is in order to rule out a XAML issue with my context. The problem continued. I can make changes after the window is initialized and see them reflected on the UI. Any changes made to my object prior to exiting the constructor however is not reflected to the UI.
  • Johnathon Sullinger
    Johnathon Sullinger about 10 years
    I also moved my restore code and value assignments out of the constructor and into the Loaded event for the window in order to see if it was just an issue with timing. At the end of the Loaded event, I assign the valid object to the data context and the UI still just shows zeros.
  • Kevin
    Kevin about 10 years
    I see... I just mentioned that you could try changing the properties outside of page load... but then saw this comment and deleted my comment above.
  • Johnathon Sullinger
    Johnathon Sullinger about 10 years
    The object is deserialized from a saved state and then assigned to the data context. At the point where I perform that assignment, the object is fully deserialized and has valid property values. I'm pretty much at a loss on this.
  • Johnathon Sullinger
    Johnathon Sullinger about 10 years
    When I perform the deserialization, my PropertyChanged event does not get fired. Would that have something to do with it? I would think that it wouldn't considering that I assign the object as my data context after the object is properly deserialized and the properties are set correctly. I thought I would mention this anyway in case it could have some affect.
  • Johnathon Sullinger
    Johnathon Sullinger about 10 years
    I updated my OP to show a 3rd attempt, with my code in the WindowLoaded event handler.
  • Johnathon Sullinger
    Johnathon Sullinger about 10 years
    I updated my OP again to show a fix. I would still like to know why the extra line must be wrote out. I don't want to perform this assignment for each property. It makes maintaining any new properties annoying. Also note that I moved my DataContext back into the XAML and out of the code behind (not shown in my OP update) and the above fix still works. The issue appears to be a event update issue.
  • Kevin
    Kevin about 10 years
    Did you test the code that is in Update 4 - WindowLoaded() in your window constructor? If not I would try putting that code in your constructor, setting your datacontext and then not setting the SelectedCollectionDevice.
  • Johnathon Sullinger
    Johnathon Sullinger about 10 years
    I had moved all of my code into WindowLoaded() and it worked with that extra line. When I comment out the manual assignment, it fails. I have discovered where the failure happens, it is being re-set within my combo box. For some reason, this.DataContext shows the correct values while comBox.DataContext shows the values all at 0. When the combo box index is changed, the zero value object is assigned as SelectedCollectionDevice causing the UI to be zero. I'm working on a update to my OP to show this.
  • Johnathon Sullinger
    Johnathon Sullinger about 10 years
    I was able to answer the question, thanks for the help. It assisted me in narrowing down my problem!
  • Alberto Solano
    Alberto Solano about 10 years
    @JohnathonSullinger Sorry for my late response! I'm glad you fixed!
  • Assimilater
    Assimilater almost 7 years
    +1 The simplicity and high generalization of this answer helped me. I wanted the default behavior and simply had {Binding MyProp} but had to explicitly set both Mode to TwoWay and UpdateSourceTrigger to LostFocus
  • kasim
    kasim about 6 years
    My scenario is, I am also binding as above binding like selectedRow. But i have some issue, when clear my textbox and i will click cancel. It will redirected to main usercontrol. after that, i will double click same row, the textbox is blank. here, i am not saving the record. it need to fetch the value from model selected row object and bind to the textbox. but, it is failing. What needs to be done here? can u please clarify me. Thanks!
  • bilal.haider
    bilal.haider over 5 years
    Reading the first line was enough :D
  • dvdhns
    dvdhns about 5 years
    Did not realize this "TextBox is updated only when the focus on it is lost" Thanks! Now I finally understand why some examples show UpdateSourceTrigger and some do not.