【问题标题】:Binding to static property绑定到静态属性
【发布时间】:2010-10-30 11:52:38
【问题描述】:

我很难将简单的静态字符串属性绑定到文本框。

这是具有静态属性的类:

public class VersionManager
{
    private static string filterString;

    public static string FilterString
    {
        get { return filterString; }
        set { filterString = value; }
    }
}

在我的 xaml 中,我只想将此静态属性绑定到 TextBox:

<TextBox>
    <TextBox.Text>
        <Binding Source="{x:Static local:VersionManager.FilterString}"/>
    </TextBox.Text>
</TextBox>

一切都编译,但在运行时,我得到以下异常:

无法转换属性中的值 “来源”到类型的对象 'System.Windows.Markup.StaticExtension'。 对象错误 'System.Windows.Data.Binding' 在 标记文件 'BurnDisk;组件/selectversionpagefunction.xaml' 第 57 行第 29 行。

知道我做错了什么吗?

【问题讨论】:

    标签: wpf xaml data-binding


    【解决方案1】:

    你不能绑定到这样的静态。由于不涉及 DependencyObject(或实现 INotifyPropertyChanged 的对象实例),因此绑定基础架构无法收到更新通知。

    如果该值没有改变,只需放弃绑定并直接在Text 属性内使用x:Static。将下面的 app 定义为 VersionManager 类的命名空间(和程序集)位置。

    <TextBox Text="{x:Static app:VersionManager.FilterString}" />
    

    如果值确实发生了变化,我建议创建一个单例来包含该值并绑定到该值。

    单例示例:

    public class VersionManager : DependencyObject {
        public static readonly DependencyProperty FilterStringProperty =
            DependencyProperty.Register( "FilterString", typeof( string ),
            typeof( VersionManager ), new UIPropertyMetadata( "no version!" ) );
        public string FilterString {
            get { return (string) GetValue( FilterStringProperty ); }
            set { SetValue( FilterStringProperty, value ); }
        }
    
        public static VersionManager Instance { get; private set; }
    
        static VersionManager() {
            Instance = new VersionManager();
        }
    }
    
    <TextBox Text="{Binding Source={x:Static local:VersionManager.Instance},
                            Path=FilterString}"/>
    

    【讨论】:

    • 真的吗?我已经能够绑定到与我的示例非常相似的静态 Int32.MaxValue: 就是这样因为这是一种方式而工作?
    • 是的,任何双向绑定都需要绑定上的 Path 属性值。 Source 需要是一个包含 Path 指定的属性的对象。指定 OneWay 会消除该限制。
    • 另外,很抱歉更新较晚,但我用示例更新了上述答案。
    • 有没有办法绑定静态字符串。我有一个多重绑定,其中一个输入是固定字符串。
    【解决方案2】:

    如果需要双向绑定,则必须提供路径。

    如果类不是静态的,则对静态属性进行双向绑定有一个技巧:在资源中声明该类的虚拟实例,并将其用作绑定源。

    <Window.Resources>
        <local:VersionManager x:Key="versionManager"/>
    </Window.Resources>
    ...
    
    <TextBox Text="{Binding Source={StaticResource versionManager}, Path=FilterString}"/>
    

    【讨论】:

    • 这个答案更适合我的情况,因为我不想将 DependencyObject 引入我的源类。感谢您的提示!
    • 请注意,这将使您的文本框能够将值推回静态属性,但不会在源值更改时更新文本框。
    • 没关系,在这种情况下,我只需要从文本框到源的绑定。如果我希望绑定以另一种方式工作,我知道需要以下方法之一:INotifyPropertyChanged、Changed 事件或依赖属性。
    • 注意:此解决方案不适用于 MVVM 情况,因为您通常无权访问要绑定的对象的类型。
    • @thomas 我很想让它为我工作,但不能。我在这里发布了我的困境作为另一个问题:stackoverflow.com/questions/34656670/…
    【解决方案3】:

    在 .NET 4.5 中,可以绑定到静态属性 read more

    您可以使用静态属性作为数据绑定的来源。这 数据绑定引擎识别属性值何时更改,如果 引发静态事件。例如,如果类 SomeClass 定义了一个 静态属性叫做 MyProperty,SomeClass 可以定义一个静态事件 当 MyProperty 的值更改时引发。静态事件 可以使用以下任一签名:

    public static event EventHandler MyPropertyChanged; 
    public static event EventHandler<PropertyChangedEventArgs> StaticPropertyChanged; 
    

    请注意,在第一种情况下,该类公开了一个名为 将 EventArgs 传递给事件处理程序的 PropertyNameChanged。 在第二种情况下,该类公开了一个名为 StaticPropertyChanged 将 PropertyChangedEventArgs 传递给 事件处理程序。实现静态属性的类可以选择 使用任一方法引发属性更改通知。

    【讨论】:

    【解决方案4】:

    您可以使用ObjectDataProvider 类,它是MethodName 属性。它可能看起来像这样:

    <Window.Resources>
       <ObjectDataProvider x:Key="versionManager" ObjectType="{x:Type VersionManager}" MethodName="get_FilterString"></ObjectDataProvider>
    </Window.Resources>
    

    声明的对象数据提供者可以这样使用:

    <TextBox Text="{Binding Source={StaticResource versionManager}}" />
    

    【讨论】:

      【解决方案5】:

      如果您使用的是本地资源,可以参考如下:

      <TextBlock Text="{Binding Source={x:Static prop:Resources.PerUnitOfMeasure}}" TextWrapping="Wrap" TextAlignment="Center"/>
      

      【讨论】:

        【解决方案6】:

        可能有两种方法/语法来绑定static 属性。如果 p 是类 MainWindow 中的 static 属性,则 textboxbinding 将是:

        1.

        <TextBox Text="{x:Static local:MainWindow.p}" />
        

        2.

        <TextBox Text="{Binding Source={x:Static local:MainWindow.p},Mode=OneTime}" />
        

        【讨论】:

          【解决方案7】:

          看看我的项目CalcBinding,它提供给你写复杂的Path属性值表达式,包括静态属性、源属性、Math等。所以,你可以这样写:

          <TextBox Text="{c:Binding local:VersionManager.FilterString}"/>
          

          祝你好运!

          【讨论】:

            【解决方案8】:

            从 WPF 4.5 开始,您可以直接绑定到静态属性,并在属性更改时自动更新绑定。您确实需要手动连接更改事件以触发绑定更新。

            public class VersionManager
            {
                private static String _filterString;        
            
                /// <summary>
                /// A static property which you'd like to bind to
                /// </summary>
                public static String FilterString
                {
                    get
                    {
                        return _filterString;
                    }
            
                    set
                    {
                        _filterString = value;
            
                        // Raise a change event
                        OnFilterStringChanged(EventArgs.Empty);
                    }
                }
            
                // Declare a static event representing changes to your static property
                public static event EventHandler FilterStringChanged;
            
                // Raise the change event through this static method
                protected static void OnFilterStringChanged(EventArgs e)
                {
                    EventHandler handler = FilterStringChanged;
            
                    if (handler != null)
                    {
                        handler(null, e);
                    }
                }
            
                static VersionManager()
                {
                    // Set up an empty event handler
                    FilterStringChanged += (sender, e) => { return; };
                }
            
            }
            

            您现在可以像绑定其他任何属性一样绑定您的静态属性:

            <TextBox Text="{Binding Path=(local:VersionManager.FilterString)}"/>
            

            【讨论】:

            • VersionManager 类可以是静态的,并且一切正常。注意路径定义中的大括号Path=(local:VersionManager.FilterString)。有谁知道为什么需要它们?
            • 路径定义中需要大括号,因为属性是静态的,见here
            • @Matt 我怎么知道我的 wpf 版本?
            • 另请注意,与“普通”绑定不同,您不能在此处省略 Path=
            【解决方案9】:

            .NET 4.5 + 的正确变体

            C#代码

            public class VersionManager
            {
                private static string filterString;
            
                public static string FilterString
                {
                    get => filterString;
                    set
                    {
                        if (filterString == value)
                            return;
            
                        filterString = value;
            
                        StaticPropertyChanged?.Invoke(null, FilterStringPropertyEventArgs);
                    }
                }
            
                private static readonly PropertyChangedEventArgs FilterStringPropertyEventArgs = new PropertyChangedEventArgs (nameof(FilterString));
                public static event PropertyChangedEventHandler StaticPropertyChanged;
            }
            

            XAML 绑定(注意大括号是 (),而不是 {})

            <TextBox Text="{Binding Path=(yournamespace:VersionManager.FilterString)}" />
            

            【讨论】:

            • 对您的代码稍作更改以正确调用 EventHandler。
            • 尝试了很多不同的解决方案,这个成功了。 PropertyChangedEventHandler 对我有用。干杯。
            【解决方案10】:

            最简单的答案(.net 4.5 及更高版本):

                static public event EventHandler FilterStringChanged;
                static string _filterString;
                static public string FilterString
                {
                    get { return _filterString; }
                    set
                    {
                        _filterString= value;
                        FilterStringChanged?.Invoke(null, EventArgs.Empty);
                    }
                }
            

            和 XAML:

                <TextBox Text="{Binding Path=(local:VersionManager.FilterString)}"/>
            

            不要忽略括号

            【讨论】:

              【解决方案11】:

              另一种解决方案是创建一个像这样实现 PropertyChanger 的普通类

              public class ViewProps : PropertyChanger
              {
                  private string _MyValue = string.Empty;
                  public string MyValue
                  {
                      get { 
                          return _MyValue
                      }
                      set
                      {
                          if (_MyValue == value)
                          {
                              return;
                          }
                          SetProperty(ref _MyValue, value);
                      }
                  }
              }
              

              然后在你不会的地方创建一个类的静态实例

              public class MyClass
              {
                  private static ViewProps _ViewProps = null;
                  public static ViewProps ViewProps
                  {
                      get
                      {
                          if (_ViewProps == null)
                          {
                              _ViewProps = new ViewProps();
                          }
                          return _ViewProps;
                      }
                  }
              }
              

              现在将其用作静态属性

              <TextBlock  Text="{x:Bind local:MyClass.ViewProps.MyValue, Mode=OneWay}"  />
              

              如果需要,这里是 PropertyChanger 的实现

              public abstract class PropertyChanger : INotifyPropertyChanged
              {
                  public event PropertyChangedEventHandler PropertyChanged;
              
                  protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
                  {
                      if (object.Equals(storage, value)) return false;
              
                      storage = value;
                      OnPropertyChanged(propertyName);
                      return true;
                  }
              
                  protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
                  {
                      PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
                  }
              }
              

              【讨论】:

                猜你喜欢
                • 2014-09-10
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2014-06-29
                • 2015-10-15
                • 2011-04-21
                • 2016-01-06
                相关资源
                最近更新 更多