Robbert Dam
Robbert Dam

Reputation: 4107

The proper way to do this - binding to properties

I have 2 controls that both bind to the same dependency property "Weather". In the first control you put the current weather, the other one displays a forecast.

In my XAML of the first control I bind a TextBox that contains the "Humidity" as follows.

<TextBox Text="{Binding Weather.Humidity}" />

Whenever the Humidity changes, I want the other control to do something, however changing only Humidity does not change the Weather - so the other control it not notified. Changing the Humidity should change the entire forecast.

(I'm not actually writing a weather app., but just using the above as an example)

My question: what is the proper way to do this? The only way I can think of is setting a SourceUpdated event handler on the TextBox that touches the Weather property. Is there a more elegant way to do this?

Thanks

Upvotes: 0

Views: 460

Answers (3)

Tawani
Tawani

Reputation: 11198

A simple binding scenario will be something like this:

WPF Simple DataBinding

This might help:

<Window x:Class="BindingSample.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    WindowStartupLocation="CenterScreen"    
    Title="BindingDemo" Height="300" Width="300">
    <Grid>
        <StackPanel Margin="20">
            <Slider Name="fontSizeSlider" Minimum="0" 
            Maximum="100" Value="{Binding Path=Weather.Humidity, Mode=TwoWay}"/>

            <Label Content="Enter Humidity (between 0 to 100)" />
            <TextBox x:Name="_humidity" 
                Text="{Binding Path=Weather.Humidity, 
                               Mode=TwoWay, 
                               UpdateSourceTrigger=PropertyChanged}"
            />
            <TextBlock Text=" "/>
            <Label Content="Forecast: " />
            <TextBlock 
                Text="{Binding Path=Weather.Forecast}" 
                Foreground="Blue" 
                FontSize="{Binding ElementName=_humidity,Path=Text}" />            
        </StackPanel>
    </Grid>
</Window>

And the Weather class can be something as follows:

public class DummyViewModel
{
    public Weather Weather { get; set; }        

    public DummyViewModel() 
    { 
        this.Weather = new Weather();
    }

    public DummyViewModel(int humidity):this() 
    {
        this.Weather.Humidity = humidity;
    }
}

public class Weather : INotifyPropertyChanged
{
    #region - Fields -

    private string _forecast;        
    private decimal _humidity;        


    #endregion // Fields

    #region - Constructor         -

    #endregion // Constructor

    #region - Properties -

    public string Forecast
    {
        get { return _forecast; }
        set
        {
            if (value == _forecast)
                return;

            _forecast = value;

            this.OnPropertyChanged("Forecast");
        }
    }


    public decimal Humidity
    {
        get { return _humidity; }
        set
        {
            if (value == _humidity)
                return;

            _humidity = value;

            this.OnPropertyChanged("Humidity");
            UpdateForeCast();
        }
    }        

    #endregion // Properties

    #region - Private Methods -

    private void UpdateForeCast()
    {
        if (this.Humidity < 0 || this.Humidity > 100)
            this.Forecast = "Unknown";
        else if (this.Humidity >= 70)
            this.Forecast = "High";
        else if (this.Humidity < 40)
            this.Forecast = "Low";
        else 
            this.Forecast = "Average";
    }

    #endregion

    #region INotifyPropertyChanged Members

    /// <summary>
    /// Raised when a property on this object has a new value.
    /// </summary>
    public event PropertyChangedEventHandler PropertyChanged;

    /// <summary>
    /// Raises this object's PropertyChanged event.
    /// </summary>
    /// <param name="propertyName">The property that has a new value.</param>
    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = this.PropertyChanged;
        if (handler != null)
        {
            var e = new PropertyChangedEventArgs(propertyName);
            handler(this, e);
        }
    }

    #endregion // INotifyPropertyChanged Members
}

Then you can this:

public Window1()
{
    InitializeComponent();

    this.DataContext = new DummyViewModel(40);
}

Or M-V-VM style

Window1 view = new Window1();
view.DataContext new DummyViewModel(40);
view.Show();

Upvotes: 0

user117500
user117500

Reputation: 26

I assume that the reason why you want the other control to do something is because humidity affects some other property of the weather/forecast. In this case, you implement INotifyPropertyChanged, as in rmoore's answer, and you make sure that when humidity is modified, it either explicitly changes the other property, triggering its notification update, or it sends a notification for its update, like this:

private int myHumidity;
            public int Humidity
            {
                    get
                    {
                            return this.myHumidity;
                    }
                    set
                    {
                            this.myHumidity = value;
                            NotifyPropertyChanged("Humidity");
                            NotifyPropertyChanged("MyOtherProperty");
                    }
            }

Upvotes: 1

rmoore
rmoore

Reputation: 15393

The default value for the UpdateSourceTrigger property on a TextBox binding is 'LostFocus' one thing you should do is change this to PropertyChanged, then the Humidity property will reflect any changes as you enter them in the TextBox.

Next, you want to make sure that your Weather Class implements INotifyPropertyChanged, like so:

    public class Weather : INotifyPropertyChanged
    {
        private int myHumidity;
        public int Humidity
        {
            get
            {
                return this.myHumidity;
            }
            set
            {
                this.myHumidity = value;
                NotifyPropertyChanged("Humidity");
            }
        }

        private void NotifyPropertyChanged(String info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion
    }

This will ensure that the UI is notified of any changes to the Humidity property.

Upvotes: 1

Related Questions