6

is there an issue with two way binding to the IsChecked property on a ToggleButton in .NET 3.5?

I have this XAML:

 <ToggleButton 
                    Name="tbMeo"
                    Command="{Binding FilterOnSatelliteTypeCmd}" 
                    IsChecked="{Binding ShowMeoDataOnly, Mode=TwoWay}"
                    ToolTip="Show MEO data only">
                    <Image Source="../images/32x32/Filter_Meo.png" Height="16" />
                </ToggleButton>

I have a ViewModel with the following property:

 private bool _showMeoDataOnly;
    public bool ShowMeoDataOnly
    {
        get { return _showMeoDataOnly; }
        set
        {
            if (_showMeoDataOnly != value)
            {
                _showMeoDataOnly = value;
                RaisePropertyChangedEvent("ShowMeoDataOnly");
            }
        }
    }

If I click on the ToggleButton, the value of ShowMeoDataOnly is set accordingly. However, if I set ShowMeoDataOnly to true from code behind, the ToggleButton's visual state does not change to indicate that IsChecked is true. However, if I manually set the ToggleButton's IsChecked property instead of setting ShowMeoDataOnly to true in code behind, the button's visual state changes accordingly.

Unfortunately, switching over to .NET 4/4.5 is not an option right now, so I cannot confirm if this is a .NET 3.5 problem.

Is there anything wrong with my code?

Klaus Nji
  • 18,107
  • 29
  • 105
  • 185
  • Where do you set Data Context? – Bob. Oct 30 '12 at 20:31
  • I set the DataContext immediately after the view containing posted XAML loads. If it was a DataContext issue FilterOnSatelliteTypeCmd command bound to the ToggleButton will not work when button is clicked but this is not the case. – Klaus Nji Oct 31 '12 at 12:34
  • Does your Output tab in Visual Studio say you have errors? i.e. BindingExpression errors or the like? – Bob. Oct 31 '12 at 12:56
  • .. and that is the weird thing. No binding errors for this particular view. – Klaus Nji Oct 31 '12 at 15:25
  • 1
    Bob, figured it out, thanks. Was passing wrong string to RaisePropertyChangedEvent. – Klaus Nji Oct 31 '12 at 16:00

2 Answers2

7

Using a .NET 3.5 project to test this and the binding seems to work for me. Do you have INotifyPropertyChanged implemented on your ViewModel and use it appropriately when ShowMeoDataOnly gets set? You didn't post all your code, so it's hard to tell what the ViewModel is doing.

Here's what I have that worked. When I run the application, the button is selected. That's because ViewModelBase implements INotifyPropertyChanged and I do base.OnPropertyChanged("ShowMeoDataOnly") when the property is set.

MainWindow.xaml

<Window x:Class="ToggleButtonIsCheckedBinding.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:ToggleButtonIsCheckedBinding"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <local:MainWindowViewModel />
    </Window.DataContext>
    <Grid>
        <ToggleButton IsChecked="{Binding ShowMeoDataOnly, Mode=TwoWay}">
            Show Meo Data Only
        </ToggleButton>
    </Grid>
</Window>

MainWindowViewModel.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ToggleButtonIsCheckedBinding
{
    class MainWindowViewModel : ViewModelBase
    {
        bool _showMeoDataOnly;
        public bool ShowMeoDataOnly {
            get
            {
                return _showMeoDataOnly;
            }

            set
            {
                _showMeoDataOnly = value;
                base.OnPropertyChanged("ShowMeoDataOnly");
            }
        }

        public MainWindowViewModel()
        {
            ShowMeoDataOnly = true;
        }
    }
}

ViewModelBase.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.ComponentModel;

namespace ToggleButtonIsCheckedBinding
{
    /// <summary>
    /// Base class for all ViewModel classes in the application.
    /// It provides support for property change notifications 
    /// and has a DisplayName property.  This class is abstract.
    /// </summary>
    public abstract class ViewModelBase : INotifyPropertyChanged, IDisposable
    {
        #region Constructor

        protected ViewModelBase()
        {
        }

        #endregion // Constructor

        #region DisplayName

        /// <summary>
        /// Returns the user-friendly name of this object.
        /// Child classes can set this property to a new value,
        /// or override it to determine the value on-demand.
        /// </summary>
        public virtual string DisplayName { get; protected set; }

        #endregion // DisplayName

        #region Debugging Aides

        /// <summary>
        /// Warns the developer if this object does not have
        /// a public property with the specified name. This 
        /// method does not exist in a Release build.
        /// </summary>
        [Conditional("DEBUG")]
        [DebuggerStepThrough]
        public void VerifyPropertyName(string propertyName)
        {
            // Verify that the property name matches a real,  
            // public, instance property on this object.
            if (TypeDescriptor.GetProperties(this)[propertyName] == null)
            {
                string msg = "Invalid property name: " + propertyName;

                if (this.ThrowOnInvalidPropertyName)
                    throw new Exception(msg);
                else
                    Debug.Fail(msg);
            }
        }

        /// <summary>
        /// Returns whether an exception is thrown, or if a Debug.Fail() is used
        /// when an invalid property name is passed to the VerifyPropertyName method.
        /// The default value is false, but subclasses used by unit tests might 
        /// override this property's getter to return true.
        /// </summary>
        protected virtual bool ThrowOnInvalidPropertyName { get; private set; }

        #endregion // Debugging Aides

        #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)
        {
            this.VerifyPropertyName(propertyName);

            PropertyChangedEventHandler handler = this.PropertyChanged;
            if (handler != null)
            {
                var e = new PropertyChangedEventArgs(propertyName);
                handler(this, e);
            }
        }

        #endregion // INotifyPropertyChanged Members

        #region IDisposable Members

        /// <summary>
        /// Invoked when this object is being removed from the application
        /// and will be subject to garbage collection.
        /// </summary>
        public void Dispose()
        {
            this.OnDispose();
        }

        /// <summary>
        /// Child classes can override this method to perform 
        /// clean-up logic, such as removing event handlers.
        /// </summary>
        protected virtual void OnDispose()
        {
        }

#if DEBUG
        /// <summary>
        /// Useful for ensuring that ViewModel objects are properly garbage collected.
        /// </summary>
        ~ViewModelBase()
        {
            string msg = string.Format("{0} ({1}) ({2}) Finalized", this.GetType().Name, this.DisplayName, this.GetHashCode());
            System.Diagnostics.Debug.WriteLine(msg);
        }
#endif

        #endregion // IDisposable Members
    }
}

(Note: ViewModelBase is pulled from this project: http://msdn.microsoft.com/en-us/magazine/dd419663.aspx )

Kyle Tolle
  • 694
  • 2
  • 7
  • 17
  • 2
    There seems to be an issue with RadioButton IsChecked binding in .NET 3.5. These links are for RadioButtons but they might give an alternative is the code above still doesn't work for you: http://blogs.msdn.com/b/mthalman/archive/2008/09/04/wpf-data-binding-with-radiobutton.aspx found from http://stackoverflow.com/questions/883246/mvvm-radiobuttons found from http://stackoverflow.com/questions/2284752/mvvm-binding-radio-buttons-to-a-view-model – Kyle Tolle Oct 30 '12 at 22:54
  • 1
    Kyle, thanks for response. Yes, my ViewModelBase class implements INotifyPropertyChanged, hence the call to RaisePropertyChangedEvent. – Klaus Nji Oct 31 '12 at 12:45
  • Kyle, I was passing the incorrect string to RaisePropertyChangedEvent. Your VerifyPropertName method helped me catch this. Interestingly enough, I have this same method on a base class I call Observable but this ViewModel does not derive from it. – Klaus Nji Oct 31 '12 at 15:57
  • It wasn't apparent to me what might have been the issue since most of the ViewModel code wasn't posted. That ViewModelBase code is in no way mine, but I'm glad it helped! – Kyle Tolle Oct 31 '12 at 17:23
3

Verify that you DataContext is set up correctly.

DataContext = this;

... in your MainWindow.xaml.cs constructor is the easiest way, assuming the code we're looking at is in the MainWindow class.

Nicholas Pappas
  • 10,439
  • 12
  • 54
  • 87