locked
Updating base class property from derived class databinding RRS feed

  • Question

  • I am having issues with getting a WPF MVVM application to update properly.  I have a Base class:

    public class ViewModelBase : INotifyPropertyChanged
        {
            private Configuration _config;
    
            public string StoragePath = Environment.GetFolderPath(Environment.SpecialFolder.Personal) + "\\Learfield";
            public string ConfigurationFile = "configuration.xml";
            public event PropertyChangedEventHandler PropertyChanged;        
    
            public Configuration Config
            {
                get { return _config; }
                set
                {
                    if(_config == null || !_config.Equals(value))
                    {
                        _config = value;
                        OnPropertyChanged(new PropertyChangedEventArgs("Config"));
                    }
                }
            }
    
            public ViewModelBase()
            {
                Config = XMLUtils.LoadConfiguration(StoragePath, ConfigurationFile);
                GlobalContext.Properties["StoragePath"] = StoragePath;
            }
    
            public void OnPropertyChanged(PropertyChangedEventArgs e)
            {
                if(PropertyChanged != null)
                {
                    PropertyChanged(this, e);
                }
            }
        }

    I have a derived ViewModel class called OptionsWindowViewModel:

    public class OptionsWindowViewModel : ViewModelBase, INotifyPropertyChanged
        {
            private ILog log = LogHelper.GetLogger();
            private Configuration _config;
    
            public new Configuration Config
            {
                get { return _config; }
                set
                {
                    if (_config == null || !_config.Equals(value))
                    {
                        _config = value;
                        OnPropertyChanged(new PropertyChangedEventArgs("Config"));
                    }
                }
            }
            //public event PropertyChangedEventHandler PropertyChanged;
            public event EventHandler RequestClose;
            public SaveCommand SaveCommand { get; set; }
    
            public OptionsWindowViewModel()
            {
                this.Config = base.Config;
                SaveCommand = new SaveCommand(this);
            }
    
            public void OnRequestClose(EventArgs e)
            {
                RequestClose(this, e);
            }
    
            //public void OnPropertyChanged(PropertyChangedEventArgs e)
            //{
            //    if (PropertyChanged != null)
            //    {
            //        PropertyChanged(this, e);
            //    }
            //}
    
            public void Save()
            {
                if (!Config.IsScheduleEnabled && Config.ScheduledStart > DateTime.Now)
                {
                    MessageBoxResult result = MessageBox.Show("You have entered a future start date, but have not enabled the scheduler.  Do you wish to enable it now?\n\nSelect Yes to enable the Scheduler.  Select No to close this window without enabling it.  Select Cancel to return to the previous window.", "Enable Scheduler?", MessageBoxButton.YesNoCancel);
    
                    if (result == MessageBoxResult.Yes)
                    {
                        Config.IsScheduleEnabled = true;
                    }
                    else
                    {
                        return;
                    }
                }
    
                if(Config.ScheduledStart < DateTime.Now && Config.IsScheduleEnabled)
                {
                    MessageBoxResult result = MessageBox.Show("The scheduled start date you selected has already passed.  You must start playback manually if playback has not already been initiated.", "Schedule Error", MessageBoxButton.OKCancel);
                    if (result == MessageBoxResult.Cancel)
                    {
                        return;
                    }
                }
    
                if (Config.ScheduledEnd <= Config.ScheduledStart && Config.IsScheduleEnabled)
                {
                    MessageBoxResult result = MessageBox.Show("The scheduled end is less than or equal to the scheduled start. The scheduler will be disabled.", "Player Warning", MessageBoxButton.OKCancel);
    
                    if (result == MessageBoxResult.OK)
                    {
                        Config.IsScheduleEnabled = false;
                    }
                    else
                    {
                        return;
                    }
                }
    
                XMLUtils.SaveConfiguration(Config, StoragePath, ConfigurationFile);
                base.Config = this.Config;
                log.Info("Configuration saved.");
                OnRequestClose(new EventArgs());
            }
    
            public void OnClosing(object sender, CancelEventArgs e)
            {
                Configuration newConfig = XMLUtils.LoadConfiguration(StoragePath, ConfigurationFile);
                if (!newConfig.Equals(Config))
                {
                    var result = MessageBox.Show("You have made changes to the configuration.  Do you wish to save these changes?\n\n**WARNING** Selecting No will close this window without saving the changes.",
                            "Save Configuration?", MessageBoxButton.YesNoCancel);
    
                    if(result == MessageBoxResult.Yes)
                    {
                        XMLUtils.SaveConfiguration(Config, StoragePath, ConfigurationFile);
                    }
                    else if(result == MessageBoxResult.Cancel)
                    {
                        e.Cancel = true;
                    }
                }
                
            }
        }

    I also have an OptionsWindow View.

    <Window x:Class="CCBDPlayer.OptionsWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:CCBDPlayer"
            xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
            mc:Ignorable="d"
            Title="OptionsWindow" Height="400" Width="350">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="3*" />
                <RowDefinition Height="4*" />
                <RowDefinition Height="2*" />
                <RowDefinition Height="1*" />
            </Grid.RowDefinitions>
            <Grid Grid.Row="0">
                <Grid.RowDefinitions>
                    <RowDefinition Height="1*" />
                    <RowDefinition Height="1*" />
                    <RowDefinition Height="1*" />
                    <RowDefinition Height="1*" />
                </Grid.RowDefinitions>
                <Label Grid.Row="0" Content="Primary CCBD Link" />
                <TextBox Grid.Row="1" Text="{Binding Config.PrimaryURL}" Width="300" HorizontalAlignment="Left" Margin="5"/>
                <Label Grid.Row="2" Content="Secondary CCBD Link" />
                <TextBox Grid.Row="3" Text="{Binding Config.SecondaryURL}" Width="300" HorizontalAlignment="Left" Margin="5"/>
            </Grid>
            <GroupBox Header="Scheduled Start" Grid.Row="1" Margin="5,0,5,0">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="2*" />
                        <RowDefinition Height="2*" />
                        <RowDefinition Height="2*" />
                    </Grid.RowDefinitions>
                    <CheckBox Grid.Row="0" Content="Enable Scheduled Start" Margin="5" VerticalAlignment="Center" IsChecked="{Binding Config.IsScheduleEnabled}"/>
                    <Grid Grid.Row="1">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="100" />
                            <ColumnDefinition Width="1*" />
                        </Grid.ColumnDefinitions>
                        <Label Grid.Column="0" Content="Scheduled Start:" HorizontalAlignment="Left"/>
                        <xctk:DateTimePicker Grid.Column="1" Value="{Binding Config.ScheduledStart}" Height="20" VerticalAlignment="Top" Name="scheduledStartDateTimePicker"/>
                    </Grid>
                    <Grid Grid.Row="2">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="100" />
                            <ColumnDefinition Width="1*" />
                        </Grid.ColumnDefinitions>
                        <Label Grid.Column="0" Content="Scheduled End:" HorizontalAlignment="Left"/>
                        <xctk:DateTimePicker Grid.Column="1" Value="{Binding Config.ScheduledEnd}" Height="20" VerticalAlignment="Top" Name="scheduledEndDateTimePicker"/>
                    </Grid>
    
                </Grid>
            </GroupBox>
            <GroupBox Grid.Row="2" Header="Buffering" Margin="5,0,5,0">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="165" />
                        <ColumnDefinition Width="1*" />
                    </Grid.ColumnDefinitions>
                    <Label Content="Number of Seconds to Buffer:" Grid.Column="0" />
                    <xctk:IntegerUpDown Grid.Column="1" Height="25"  Value="{Binding Config.Buffer}" VerticalAlignment="Top"></xctk:IntegerUpDown>
                </Grid>
            </GroupBox>
            <Button Grid.Row="3" HorizontalAlignment="right" Margin="0,0,5,0" 
                    Content="Save" Width="75" Height="25" Command="{Binding SaveCommand}"/>
        </Grid>
    </Window>

    When I attempt to change the value of the ScheduledStartDateTime, this does not get modified in the base class.  The property does not change.  Please help.  I need this to propagate up to the base class and the new value readable in a sister derived from the ViewModelBase.


    Wednesday, July 20, 2016 8:15 PM

Answers

  • Hi Aaron Runmford,

    >>"When I attempt to change the value of the ScheduledStartDateTime, this does not get modified in the base class. "

    In the derived class, I saw you used new keyword to re-define Config property.

    public new Configuration Config
    

    If we create a new instance of OptionsWindowViewModel. There will be 2 Config properties in the instance. One is created by the base class and the other one is created by the derived class. All the properties changed in view will update the second one. So it will not get modified in the base class.

    >>"I need this to propagate up to the base class and the new value readable in a sister derived from the ViewModelBase."

    Do you want to share data between different models or windows? It is not suitable to share data using class inheritance. Inheritance is used to code reuse and reduce the complexity of the code. If you want to share data between different models. I suggest you create a static class with static properties which will used for multi instances and windows.

    public static class SharedClass
    {
        public static DateTime SharedScheduledStartDateTime { get; set; }
    }

    If the ScheduledStartDateTime changed in the window, you need to change the value of SharedScheduledStartDateTime manually.

    Best Regards,
    Li Wang


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey.

    • Proposed as answer by DotNet Wang Saturday, July 30, 2016 5:35 AM
    • Marked as answer by DotNet Wang Monday, August 1, 2016 1:37 AM
    Thursday, July 21, 2016 6:25 AM