【问题标题】:WPF ToggleButton.IsChecked binding does not workWPF ToggleButton.IsChecked 绑定不起作用
【发布时间】:2012-10-20 06:29:06
【问题描述】:

在 .NET 3.5 中,两种方式绑定到 ToggleButton 上的 IsChecked 属性是否存在问题?

我有这个 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>

我有一个具有以下属性的 ViewModel:

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

如果我单击 ToggleButton,ShowMeoDataOnly 的值会相应设置。但是,如果我从后面的代码中将 ShowMeoDataOnly 设置为 true,则 ToggleButton 的视觉状态不会更改以指示 IsChecked 为 true。但是,如果我手动设置 ToggleButton 的 IsChecked 属性,而不是在后面的代码中将 ShowMeoDataOnly 设置为 true,则按钮的视觉状态会相应更改。

不幸的是,现在不能切换到 .NET 4/4.5,所以我无法确认这是否是 .NET 3.5 的问题。

我的代码有什么问题吗?

【问题讨论】:

  • 你在哪里设置数据上下文?
  • 我在包含已发布 XAML 的视图加载后立即设置 DataContext。如果是 DataContext 问题,则在单击按钮时,绑定到 ToggleButton 的 FilterOnSatelliteTypeCmd 命令将不起作用,但事实并非如此。
  • Visual Studio 中的“输出”选项卡是否显示有错误?即 BindingExpression 错误等?
  • .. 这很奇怪。此特定视图没有绑定错误。
  • 鲍勃,想通了,谢谢。向 RaisePropertyChangedEvent 传递了错误的字符串。

标签: wpf data-binding togglebutton


【解决方案1】:

使用 .NET 3.5 项目对此进行测试,绑定似乎对我有用。您是否在 ViewModel 上实现了 INotifyPropertyChanged 并在设置 ShowMeoDataOnly 时适当地使用它?您没有发布所有代码,因此很难判断 ViewModel 在做什么。

这就是我所做的工作。当我运行应用程序时,该按钮被选中。那是因为ViewModelBase 实现了INotifyPropertyChanged 而我在设置属性时执行base.OnPropertyChanged("ShowMeoDataOnly")

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

(注意:ViewModelBase 是从这个项目中提取的:http://msdn.microsoft.com/en-us/magazine/dd419663.aspx

【讨论】:

  • .NET 3.5 中的 RadioButton IsChecked 绑定似乎存在问题。这些链接适用于 RadioButtons,但它们可能会提供另一种选择,即上面的代码仍然对您不起作用:blogs.msdn.com/b/mthalman/archive/2008/09/04/… found from stackoverflow.com/questions/883246/mvvm-radiobuttons found from stackoverflow.com/questions/2284752/…
  • 凯尔,感谢您的回复。是的,我的 ViewModelBase 类实现了 INotifyPropertyChanged,因此调用了 RaisePropertyChangedEvent。
  • Kyle,我将不正确的字符串传递给 RaisePropertyChangedEvent。您的 VerifyPropertName 方法帮助我抓住了这一点。有趣的是,我在一个名为 Observable 的基类上有同样的方法,但这个 ViewModel 不是从它派生的。
  • 由于大部分 ViewModel 代码都没有发布,因此对我来说并不清楚可能是什么问题。那个 ViewModelBase 代码绝不是我的,但我很高兴它有帮助!
【解决方案2】:

验证您的 DataContext 是否设置正确。

DataContext = this;

... 在 MainWindow.xaml.cs 构造函数中是最简单的方法,假设我们正在查看的代码在 MainWindow 类中。

【讨论】:

    猜你喜欢
    • 2011-10-02
    • 1970-01-01
    • 2011-04-21
    • 2014-08-25
    • 1970-01-01
    • 1970-01-01
    • 2014-04-10
    相关资源
    最近更新 更多