8wDlpd.png
8wDFp9.png
8wDEOx.png
8wDMfH.png
8wDKte.png

为什么绑定没有生效

Hesam Eskandari 2月前

52 0

我创建了 TextBox 类的派生类 MyTextBox,并添加了一个名为 AString 的依赖属性和一个名为 ContextChangedEvent 的路由事件。在 XAML 代码中,将 Text 属性绑定到...

我创建了 TextBox 类的派生类 MyTextBox,并添加了一个名为 AString 的依赖属性和一个名为 ContextChangedEvent 的路由事件。在 XAML 代码中,Text 属性已与 AString 属性绑定,但是当我更改 Text 属性的值时,AString 属性的 setter 并未触发,这意味着绑定未生效。这是为什么?

public class MyTextBox : TextBox
{
    public static readonly RoutedEvent ContentChangedEvent;
    public event RoutedEventHandler ContentChanged
    {
        add { AddHandler(ContentChangedEvent, value); }
        remove { RemoveHandler(ContentChangedEvent, value); }
    }

    public string AString
    {
        get { return (string)GetValue(AStringProperty); }
        set
        {
            SetValue(AStringProperty, value);
            OnContentChanged();
        }
    }

    public static readonly DependencyProperty AStringProperty;

    static MyTextBox()
    {
        ContentChangedEvent = EventManager.RegisterRoutedEvent("ContentChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyTextBox));
        var a = new FrameworkPropertyMetadata("555");
        a.BindsTwoWayByDefault = true;
        AStringProperty = DependencyProperty.Register("AString", typeof(string), typeof(MyTextBox), a);
    }
    private void OnContentChanged()
    {
        RaiseEvent(new RoutedEventArgs(ContentChangedEvent, this));
    }
 <local:MyTextBox x:Name="mtb1"
                  ContentChanged="MyTextBox_ContentChanged"
                  Text="{Binding AString,
                                 RelativeSource={RelativeSource Mode=Self},
                                 Mode=TwoWay}" />

我尝试将绑定模式改为双向绑定,但没有效果

帖子版权声明 1、本帖标题:为什么绑定没有生效
    本站网址:http://xjnalaquan.com/
2、本网站的资源部分来源于网络,如有侵权,请联系站长进行删除处理。
3、会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。
4、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
5、站长邮箱:yeweds@126.com 除非注明,本帖由Hesam Eskandari在本站《wpf》版块原创发布, 转载请注明出处!
最新回复 (0)
  • \'未触发 AString 属性的 setter,这意味着绑定未生效。\' - 这意味着框架不调用 setter 并直接使用 SetValue。使用具有属性更改回调的 DependencyProperty.Register 重载

  • 我从事 WPF 开发大约有 10 年了,见过各种各样的愚蠢行为,但今天我偶然发现了一批新鲜的公牛粪便,绝对……

    我从事 WPF 开发已经有 10 年了,见过很多 各种各样 的愚蠢行为,但今天我偶然发现了一批新鲜的雄性牛粪,它们绝对是最愚蠢的。事实上,是所有存在的愚蠢行为。

    将转换器放在窗口资源列表的顶部会以某种方式破坏其后的 UI 对象的资源。

    下面是重现此问题的最简单的应用程序示例,其中包含我能找到的最少的代码:

    <Window x:Class="WpfApp1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:converters="clr-namespace:WpfApp1.Converters"
            xmlns:system="clr-namespace:System;assembly=mscorlib"
            SizeToContent="WidthAndHeight"
            WindowStartupLocation="CenterScreen">
        <Window.Resources>
            <converters:BaseConverter x:Key="test"/>
    
            <Grid x:Key="TestItemTemplateGrid" x:Shared="false">
                <Button Content="{Binding StringFormat='Button {0}'}" Width="300"/>
            </Grid>
        </Window.Resources>
    
        <ItemsControl>
            <system:Int32>0</system:Int32>
            <system:Int32>1</system:Int32>
            <system:Int32>2</system:Int32>
            <system:Int32>3</system:Int32>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <ContentControl Content="{StaticResource TestItemTemplateGrid}" Margin="0,5,0,0"/>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </Window>
    

    转换器:

    using System;
    using System.Globalization;
    using System.Windows.Data;
    using System.Windows.Markup;
    
    namespace WpfApp1.Converters
    {
        public class BaseConverter : MarkupExtension, IValueConverter
        {
            public virtual object Convert(object value, Type targetType, object parameter, CultureInfo culture)
            {
                return Binding.DoNothing;
            }
    
            public virtual object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
            {
                return Binding.DoNothing;
            }
    
            public override object ProvideValue(IServiceProvider serviceProvider)
            {
                return this;
            }
        }
    }
    

    这只是一个简单的尝试,在 ItemsControl 中加载可变数量的项目,其中每个项目都是一个 ContentControl,它将自身填充为资源中定义的 Grid。

    根据此 xaml, 应用程序 应该

    wpf app 1

    但实际上它看起来是这样的:

    wpf app 2

    这就像 x:Shared 被打破并变成现实或者别的什么,只是因为在它之前有一个转换器资源。

    我测试过的内容:

    • 转换器的键名并不重要
    • 转换器的类型和/或方法实现并不重要
    • ItemsControl 是否具有绑定的 ItemsSource 与硬编码条目无关紧要
    • 网格内的实际内容并不重要

    当然,删除 <converters:BaseConverter x:Key="test"/> 可以修复该问题,但真正的问题是: 将其放在资源列表的最后也可以修复该问题。

    ...哇。哇。?我死了吗?我在做梦吗?愚人节是被编入 WPF 的吗,但它有 bug(就像其他东西一样)而且晚了一个月?有人能解释一下吗?我快疯了。

    项目文件(以防万一):

    <Project Sdk="Microsoft.NET.Sdk">
    
      <PropertyGroup>
        <OutputType>WinExe</OutputType>
        <TargetFramework>net5.0-windows</TargetFramework>
        <UseWPF>true</UseWPF>
      </PropertyGroup>
    
    </Project>
    
  • CLR 属性只是依赖属性的代理包装器。除了获取和设置属性值之外,此包装器中不应存在任何逻辑。
    正确的实现如下:

        public class MyTextBox : TextBox
        {
            public event RoutedEventHandler ContentChanged
            {
                add => AddHandler(ContentChangedEvent, value);
                remove => RemoveHandler(ContentChangedEvent, value);
            }
            public static readonly RoutedEvent ContentChangedEvent
                = EventManager.RegisterRoutedEvent(
                    nameof(ContentChanged),
                    RoutingStrategy.Bubble,
                    typeof(RoutedEventHandler),
                    typeof(MyTextBox));
    
            public string AString
            {
                get => (string)GetValue(AStringProperty);
                set => SetValue(AStringProperty, value);// OnContentChanged();
            }
    
            public static readonly DependencyProperty AStringProperty
                = DependencyProperty.Register(
                    nameof(AString),
                    typeof(string),
                    typeof(MyTextBox),
                    new FrameworkPropertyMetadata("555")
                    {
                        BindsTwoWayByDefault = true,
                        PropertyChangedCallback = OnContentChanged
                    });
            private static void OnContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
               ((UIElement)d).RaiseEvent(new RoutedEventArgs(ContentChangedEvent, d));
            }
        }
    

    PS 请解释一下为什么需要 TextBox 中的第二个字符串属性?

  • 这样我就可以直接将其传递到绑定中,而不是将其作为资源……我通常会这样做,但今天我确实遇到了多次需要同一个转换器的情况,所以我做了资源的事情。然后它破坏了一切,因此我们现在就在这里!

  • 我明白了。我刚刚发现您的 BaseConverter 不需要成为转换器即可重现该问题。仅继承 MarkupExtension 似乎就足够了。如果是这样,那么这是 MarkupExtension 的问题,而不是 IValueConverter 的问题。

  • 顺便说一句,你应该知道顺序很重要。你可以基于另一种样式来创建一种样式,但必须先定义它。所以你不应该对此感到惊讶。

  • 如果 MarkupExtension 在资源中首先出现,则会发生错误。
    如果将 IValueConverter 和 MarkupExtension 分开,那么当先创建 IValueConverter 然后创建 MarkupExtension 时,不会出现错误:

    namespace WpfApp1.Converters
    {
        public class BaseConverter : IValueConverter
        {
            public virtual object Convert(object value, Type targetType, object parameter, CultureInfo culture)
            {
                return Binding.DoNothing;
            }
    
            public virtual object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
            {
                return Binding.DoNothing;
            }
    
        }
    
        [MarkupExtensionReturnType(typeof(BaseConverter))]
        public class BaseConverterExtension : MarkupExtension
        {
            public override object ProvideValue(IServiceProvider serviceProvider)
            {
                return new BaseConverter();
            }
        }
    }
    
        <Window.Resources>
            <converters:BaseConverter x:Key="test"/>
            <converters:BaseConverterExtension x:Key="test1"/>
    
            <Grid x:Key="TestItemTemplateGrid" x:Shared="false">
                <Button Content="{Binding StringFormat='Button {0}'}" Width="300"/>
            </Grid>
        </Window.Resources>
    

    如果按照相反的顺序创建,则会出现错误:

        <Window.Resources>
            <converters:BaseConverterExtension x:Key="test1"/>
            <converters:BaseConverter x:Key="test"/>
    
            <Grid x:Key="TestItemTemplateGrid" x:Shared="false">
                <Button Content="{Binding StringFormat='Button {0}'}" Width="300"/>
            </Grid>
        </Window.Resources>
    

    此错误不是由您的实现引起的。标记扩展的默认实现也会出现这种情况:

        <Window.Resources>
            <!--<converters:BaseConverterExtension x:Key="test1"/>-->
            <RelativeSource Mode="Self" x:Key="test1"/>
            <converters:BaseConverter x:Key="test"/>
    
            <Grid x:Key="TestItemTemplateGrid" x:Shared="false">
                <Button Content="{Binding StringFormat='Button {0}'}" Width="300"/>
            </Grid>
        </Window.Resources>
    
  • @Pojo 我说不出原因是什么。ResourceDictionary 是一个特定的集合,有其自身的限制。例如,您不能在其中创建 Binding 实例。可能还有其他限制。

返回
作者最近主题: