WPF ToggleButton.IsChecked binding does not work
Asked Answered
E

2

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?

Ericson answered 30/10, 2012 at 20:27 Comment(5)
Where do you set Data Context?Finish
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.Ericson
Does your Output tab in Visual Studio say you have errors? i.e. BindingExpression errors or the like?Finish
.. and that is the weird thing. No binding errors for this particular view.Ericson
Bob, figured it out, thanks. Was passing wrong string to RaisePropertyChangedEvent.Ericson
N
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 )

Northumbrian answered 30/10, 2012 at 22:49 Comment(4)
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: blogs.msdn.com/b/mthalman/archive/2008/09/04/… found from https://mcmap.net/q/281375/-mvvm-radiobuttons found from #2285252Northumbrian
Kyle, thanks for response. Yes, my ViewModelBase class implements INotifyPropertyChanged, hence the call to RaisePropertyChangedEvent.Ericson
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.Ericson
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!Northumbrian
S
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.

Song answered 30/10, 2012 at 20:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.